Merge "cc/sabi.go: Propagate ShouldCreateSourceAbiDump to reuseObjTag deps"
diff --git a/android/arch.go b/android/arch.go
index 9ff439c..1fbba19 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -15,6 +15,7 @@
 package android
 
 import (
+	"android/soong/bazel"
 	"encoding"
 	"fmt"
 	"reflect"
@@ -897,7 +898,7 @@
 
 			// Add the OS/Arch combinations, e.g. "android_arm64".
 			for _, archType := range osArchTypeMap[os] {
-				targets = append(targets, GetCompoundTargetName(os, archType))
+				targets = append(targets, GetCompoundTargetField(os, archType))
 
 				// Also add the special "linux_<arch>" and "bionic_<arch>" property structs.
 				if os.Linux() {
@@ -1217,7 +1218,7 @@
 	return getChildPropertyStruct(ctx, multilibProp, archType.Multilib, "multilib."+archType.Multilib)
 }
 
-func GetCompoundTargetName(os OsType, arch ArchType) string {
+func GetCompoundTargetField(os OsType, arch ArchType) string {
 	return os.Field + "_" + arch.Name
 }
 
@@ -1327,7 +1328,7 @@
 		//         key: value,
 		//     },
 		// },
-		field := GetCompoundTargetName(os, archType)
+		field := GetCompoundTargetField(os, archType)
 		userFriendlyField := "target." + os.Name + "_" + archType.Name
 		if osArchProperties, ok := getChildPropertyStruct(ctx, targetProp, field, userFriendlyField); ok {
 			result = append(result, osArchProperties)
@@ -1565,7 +1566,10 @@
 	abi         []string
 }
 
-// getNdkAbisConfig returns a list of archConfigs for the ABIs supported by the NDK.
+// getNdkAbisConfig returns the list of archConfigs that are used for bulding
+// the API stubs and static libraries that are included in the NDK. These are
+// built *without Neon*, because non-Neon is still supported and building these
+// with Neon will break those users.
 func getNdkAbisConfig() []archConfig {
 	return []archConfig{
 		{"arm", "armv7-a", "", []string{"armeabi-v7a"}},
@@ -1882,27 +1886,38 @@
 	PropertyErrorf(property, fmt string, args ...interface{})
 }
 
-// GetArchProperties returns a map of architectures to the values of the
-// properties of the 'propertySet' struct that are specific to that architecture.
+// ArchVariantProperties represents a map of arch-variant config strings to a property interface{}.
+type ArchVariantProperties map[string]interface{}
+
+// ConfigurationAxisToArchVariantProperties represents a map of bazel.ConfigurationAxis to
+// ArchVariantProperties, such that each independent arch-variant axis maps to the
+// configs/properties for that axis.
+type ConfigurationAxisToArchVariantProperties map[bazel.ConfigurationAxis]ArchVariantProperties
+
+// GetArchVariantProperties returns a ConfigurationAxisToArchVariantProperties where the
+// arch-variant properties correspond to the values of the properties of the 'propertySet' struct
+// that are specific to that axis/configuration. Each axis is independent, containing
+// non-overlapping configs that correspond to the various "arch-variant" support, at this time:
+//    arches (including multilib)
+//    oses
+//    arch+os combinations
 //
-// For example, passing a struct { Foo bool, Bar string } will return an
-// interface{} that can be type asserted back into the same struct, containing
-// the arch specific property value specified by the module if defined.
+// For example, passing a struct { Foo bool, Bar string } will return an interface{} that can be
+// type asserted back into the same struct, containing the config-specific property value specified
+// by the module if defined.
 //
 // Arch-specific properties may come from an arch stanza or a multilib stanza; properties
 // in these stanzas are combined.
 // For example: `arch: { x86: { Foo: ["bar"] } }, multilib: { lib32: {` Foo: ["baz"] } }`
 // will result in `Foo: ["bar", "baz"]` being returned for architecture x86, if the given
 // propertyset contains `Foo []string`.
-//
-// Implemented in a way very similar to GetTargetProperties().
-func (m *ModuleBase) GetArchProperties(ctx ArchVariantContext, propertySet interface{}) map[ArchType]interface{} {
+func (m *ModuleBase) GetArchVariantProperties(ctx ArchVariantContext, propertySet interface{}) ConfigurationAxisToArchVariantProperties {
 	// Return value of the arch types to the prop values for that arch.
-	archToProp := map[ArchType]interface{}{}
+	axisToProps := ConfigurationAxisToArchVariantProperties{}
 
 	// Nothing to do for non-arch-specific modules.
 	if !m.ArchSpecific() {
-		return archToProp
+		return axisToProps
 	}
 
 	dstType := reflect.ValueOf(propertySet).Type()
@@ -1920,9 +1935,10 @@
 
 	if archProperties == nil {
 		// This module does not have the property set requested
-		return archToProp
+		return axisToProps
 	}
 
+	archToProp := ArchVariantProperties{}
 	// For each arch type (x86, arm64, etc.)
 	for _, arch := range ArchTypeList() {
 		// Arch properties are sometimes sharded (see createArchPropTypeDesc() ).
@@ -1948,10 +1964,30 @@
 			mergePropertyStruct(ctx, value, propertyStruct)
 		}
 
-		archToProp[arch] = value
+		archToProp[arch.Name] = value
 	}
+	axisToProps[bazel.ArchConfigurationAxis] = archToProp
 
-	return archToProp
+	osToProp := ArchVariantProperties{}
+	archOsToProp := ArchVariantProperties{}
+	// For android, linux, ...
+	for _, os := range osTypeList {
+		if os == CommonOS {
+			// It looks like this OS value is not used in Blueprint files
+			continue
+		}
+		osToProp[os.Name] = getTargetStruct(ctx, propertySet, archProperties, os.Field)
+		// For arm, x86, ...
+		for _, arch := range osArchTypeMap[os] {
+			targetField := GetCompoundTargetField(os, arch)
+			targetName := fmt.Sprintf("%s_%s", os.Name, arch.Name)
+			archOsToProp[targetName] = getTargetStruct(ctx, propertySet, archProperties, targetField)
+		}
+	}
+	axisToProps[bazel.OsConfigurationAxis] = osToProp
+	axisToProps[bazel.OsArchConfigurationAxis] = archOsToProp
+
+	return axisToProps
 }
 
 // Returns a struct matching the propertySet interface, containing properties specific to the targetName
@@ -1989,69 +2025,3 @@
 
 	return value
 }
-
-// Properties corresponds to e.g. Target: android: {...}
-// ArchProperties corresponds to e.g. Target: android_arm: {...}, android_arm64: {...}, ...
-type TargetProperties struct {
-	Properties     interface{}
-	ArchProperties map[ArchType]interface{}
-}
-
-// GetTargetProperties returns a map of OS target (e.g. android, windows) to the
-// values of the properties of the 'propertySet' struct that are specific to
-// that OS target.
-//
-// For example, passing a struct { Foo bool, Bar string } will return an
-// interface{} that can be type asserted back into the same struct, containing
-// the os-specific property value specified by the module if defined.
-//
-// Implemented in a way very similar to GetArchProperties().
-//
-// NOTE: "Target" == OS
-func (m *ModuleBase) GetTargetProperties(ctx ArchVariantContext, propertySet interface{}) map[OsType]TargetProperties {
-	// Return value of the target types to the prop values for that target.
-	targetToProp := map[OsType]TargetProperties{}
-
-	// Nothing to do for non-target-specific modules.
-	if !m.ArchSpecific() {
-		return targetToProp
-	}
-
-	dstType := reflect.ValueOf(propertySet).Type()
-	var archProperties []interface{}
-
-	// First find the property set in the module that corresponds to the requested
-	// one. m.archProperties[i] corresponds to m.generalProperties[i].
-	for i, generalProp := range m.generalProperties {
-		srcType := reflect.ValueOf(generalProp).Type()
-		if srcType == dstType {
-			archProperties = m.archProperties[i]
-			break
-		}
-	}
-
-	if archProperties == nil {
-		// This module does not have the property set requested
-		return targetToProp
-	}
-
-	// For android, linux, ...
-	for _, os := range osTypeList {
-		if os == CommonOS {
-			// It looks like this OS value is not used in Blueprint files
-			continue
-		}
-		targetProperties := TargetProperties{
-			Properties:     getTargetStruct(ctx, propertySet, archProperties, os.Field),
-			ArchProperties: make(map[ArchType]interface{}),
-		}
-		// For arm, x86, ...
-		for _, arch := range osArchTypeMap[os] {
-			targetName := GetCompoundTargetName(os, arch)
-			targetProperties.ArchProperties[arch] = getTargetStruct(ctx, propertySet, archProperties, targetName)
-		}
-		targetToProp[os] = targetProperties
-	}
-
-	return targetToProp
-}
diff --git a/android/bazel.go b/android/bazel.go
index 8c68055..d396561 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -221,6 +221,8 @@
 	// still be generated via bp2build.
 	mixedBuildsDisabledList = []string{
 		// Currently empty, though should remain present to facilitate granular bp2build migration.
+		"libdl",         // missing libbionic_Slinker_Slibld-android_Ubp2build_Ucc_Ulibrary_Ushared.so
+		"libdl_android", // missing libbionic_Slinker_Slibld-android_Ubp2build_Ucc_Ulibrary_Ushared.so
 	}
 
 	// Used for quicker lookups
diff --git a/android/bazel_paths.go b/android/bazel_paths.go
index f4b2a7c..f93fe2b 100644
--- a/android/bazel_paths.go
+++ b/android/bazel_paths.go
@@ -100,6 +100,22 @@
 	return labels
 }
 
+// BazelLabelForModuleDeps expects two lists: modules (containing modules to include in the list),
+// and excludes (modules to exclude from the list). Both of these should contain references to other
+// modules, ("<module>" or ":<module>"). It returns a Bazel-compatible label list which corresponds
+// to dependencies on the module within the given ctx, and the excluded dependencies.
+func BazelLabelForModuleDepsExcludes(ctx BazelConversionPathContext, modules, excludes []string) bazel.LabelList {
+	moduleLabels := BazelLabelForModuleDeps(ctx, RemoveListFromList(modules, excludes))
+	if len(excludes) == 0 {
+		return moduleLabels
+	}
+	excludeLabels := BazelLabelForModuleDeps(ctx, excludes)
+	return bazel.LabelList{
+		Includes: moduleLabels.Includes,
+		Excludes: excludeLabels.Includes,
+	}
+}
+
 func BazelLabelForModuleSrcSingle(ctx BazelConversionPathContext, path string) bazel.Label {
 	return BazelLabelForModuleSrcExcludes(ctx, []string{path}, []string(nil)).Includes[0]
 }
diff --git a/android/override_module.go b/android/override_module.go
index 0a7e294..e72cb78 100644
--- a/android/override_module.go
+++ b/android/override_module.go
@@ -213,7 +213,6 @@
 // to keep them in this order and not put any order mutators between them.
 func RegisterOverridePostDepsMutators(ctx RegisterMutatorsContext) {
 	ctx.BottomUp("override_deps", overrideModuleDepsMutator).Parallel()
-	ctx.TopDown("register_override", registerOverrideMutator).Parallel()
 	ctx.BottomUp("perform_override", performOverrideMutator).Parallel()
 	// overridableModuleDepsMutator calls OverridablePropertiesDepsMutator so that overridable modules can
 	// add deps from overridable properties.
@@ -253,20 +252,11 @@
 				return
 			}
 		})
-		ctx.AddDependency(ctx.Module(), overrideBaseDepTag, *module.getOverrideModuleProperties().Base)
-	}
-}
-
-// Visits the base module added as a dependency above, checks the module type, and registers the
-// overriding module.
-func registerOverrideMutator(ctx TopDownMutatorContext) {
-	ctx.VisitDirectDepsWithTag(overrideBaseDepTag, func(base Module) {
-		if o, ok := base.(OverridableModule); ok {
+		baseModule := ctx.AddDependency(ctx.Module(), overrideBaseDepTag, *module.getOverrideModuleProperties().Base)[0]
+		if o, ok := baseModule.(OverridableModule); ok {
 			o.addOverride(ctx.Module().(OverrideModule))
-		} else {
-			ctx.PropertyErrorf("base", "unsupported base module type")
 		}
-	})
+	}
 }
 
 // Now, goes through all overridable modules, finds all modules overriding them, creates a local
diff --git a/android/variable.go b/android/variable.go
index 7658cdd..c766120 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -458,12 +458,13 @@
 // with the appropriate ProductConfigVariable.
 type ProductConfigProperty struct {
 	ProductConfigVariable string
+	FullConfig            string
 	Property              interface{}
 }
 
 // ProductConfigProperties is a map of property name to a slice of ProductConfigProperty such that
 // all it all product variable-specific versions of a property are easily accessed together
-type ProductConfigProperties map[string][]ProductConfigProperty
+type ProductConfigProperties map[string]map[string]ProductConfigProperty
 
 // ProductVariableProperties returns a ProductConfigProperties containing only the properties which
 // have been set for the module in the given context.
@@ -479,18 +480,11 @@
 
 	productVariableValues(moduleBase.variableProperties, "", &productConfigProperties)
 
-	for arch, targetProps := range moduleBase.GetArchProperties(ctx, moduleBase.variableProperties) {
-		// GetArchProperties is creating an instance of the requested type
-		// and productVariablesValues expects an interface, so no need to cast
-		productVariableValues(targetProps, arch.Name, &productConfigProperties)
-	}
-
-	for os, targetProps := range moduleBase.GetTargetProperties(ctx, moduleBase.variableProperties) {
-		// GetTargetProperties is creating an instance of the requested type
-		// and productVariablesValues expects an interface, so no need to cast
-		productVariableValues(targetProps.Properties, os.Name, &productConfigProperties)
-		for arch, archProperties := range targetProps.ArchProperties {
-			productVariableValues(archProperties, os.Name+"_"+arch.Name, &productConfigProperties)
+	for _, configToProps := range moduleBase.GetArchVariantProperties(ctx, moduleBase.variableProperties) {
+		for config, props := range configToProps {
+			// GetArchVariantProperties is creating an instance of the requested type
+			// and productVariablesValues expects an interface, so no need to cast
+			productVariableValues(props, config, &productConfigProperties)
 		}
 	}
 
@@ -519,11 +513,15 @@
 
 			// e.g. Asflags, Cflags, Enabled, etc.
 			propertyName := variableValue.Type().Field(j).Name
-			(*productConfigProperties)[propertyName] = append((*productConfigProperties)[propertyName],
-				ProductConfigProperty{
-					ProductConfigVariable: productVariableName + suffix,
-					Property:              property.Interface(),
-				})
+			if (*productConfigProperties)[propertyName] == nil {
+				(*productConfigProperties)[propertyName] = make(map[string]ProductConfigProperty)
+			}
+			config := productVariableName + suffix
+			(*productConfigProperties)[propertyName][config] = ProductConfigProperty{
+				ProductConfigVariable: productVariableName,
+				FullConfig:            config,
+				Property:              property.Interface(),
+			}
 		}
 	}
 }
diff --git a/bazel/Android.bp b/bazel/Android.bp
index b7c185a..b68d65b 100644
--- a/bazel/Android.bp
+++ b/bazel/Android.bp
@@ -7,6 +7,7 @@
     pkgPath: "android/soong/bazel",
     srcs: [
         "aquery.go",
+        "configurability.go",
         "constants.go",
         "properties.go",
     ],
diff --git a/bazel/aquery.go b/bazel/aquery.go
index bda3a1a..d1119cb 100644
--- a/bazel/aquery.go
+++ b/bazel/aquery.go
@@ -247,7 +247,7 @@
 
 func shouldSkipAction(a action) bool {
 	// TODO(b/180945121): Handle symlink actions.
-	if a.Mnemonic == "Symlink" || a.Mnemonic == "SourceSymlinkManifest" || a.Mnemonic == "SymlinkTree" {
+	if a.Mnemonic == "Symlink" || a.Mnemonic == "SourceSymlinkManifest" || a.Mnemonic == "SymlinkTree" || a.Mnemonic == "SolibSymlink" {
 		return true
 	}
 	// Middleman actions are not handled like other actions; they are handled separately as a
diff --git a/bazel/configurability.go b/bazel/configurability.go
new file mode 100644
index 0000000..282c606
--- /dev/null
+++ b/bazel/configurability.go
@@ -0,0 +1,213 @@
+// 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 bazel
+
+import (
+	"fmt"
+	"strings"
+)
+
+const (
+	// ArchType names in arch.go
+	archArm    = "arm"
+	archArm64  = "arm64"
+	archX86    = "x86"
+	archX86_64 = "x86_64"
+
+	// OsType names in arch.go
+	osAndroid     = "android"
+	osDarwin      = "darwin"
+	osFuchsia     = "fuchsia"
+	osLinux       = "linux_glibc"
+	osLinuxBionic = "linux_bionic"
+	osWindows     = "windows"
+
+	// Targets in arch.go
+	osArchAndroidArm        = "android_arm"
+	osArchAndroidArm64      = "android_arm64"
+	osArchAndroidX86        = "android_x86"
+	osArchAndroidX86_64     = "android_x86_64"
+	osArchDarwinX86_64      = "darwin_x86_64"
+	osArchFuchsiaArm64      = "fuchsia_arm64"
+	osArchFuchsiaX86_64     = "fuchsia_x86_64"
+	osArchLinuxX86          = "linux_glibc_x86"
+	osArchLinuxX86_64       = "linux_glibc_x86_64"
+	osArchLinuxBionicArm64  = "linux_bionic_arm64"
+	osArchLinuxBionicX86_64 = "linux_bionic_x86_64"
+	osArchWindowsX86        = "windows_x86"
+	osArchWindowsX86_64     = "windows_x86_64"
+
+	// This is the string representation of the default condition wherever a
+	// configurable attribute is used in a select statement, i.e.
+	// //conditions:default for Bazel.
+	//
+	// This is consistently named "conditions_default" to mirror the Soong
+	// config variable default key in an Android.bp file, although there's no
+	// integration with Soong config variables (yet).
+	conditionsDefault = "conditions_default"
+
+	ConditionsDefaultSelectKey = "//conditions:default"
+
+	productVariableBazelPackage = "//build/bazel/product_variables"
+)
+
+var (
+	// These are the list of OSes and architectures with a Bazel config_setting
+	// and constraint value equivalent. These exist in arch.go, but the android
+	// package depends on the bazel package, so a cyclic dependency prevents
+	// using those variables here.
+
+	// A map of architectures to the Bazel label of the constraint_value
+	// for the @platforms//cpu:cpu constraint_setting
+	platformArchMap = map[string]string{
+		archArm:           "//build/bazel/platforms/arch:arm",
+		archArm64:         "//build/bazel/platforms/arch:arm64",
+		archX86:           "//build/bazel/platforms/arch:x86",
+		archX86_64:        "//build/bazel/platforms/arch:x86_64",
+		conditionsDefault: ConditionsDefaultSelectKey, // The default condition of as arch select map.
+	}
+
+	// A map of target operating systems to the Bazel label of the
+	// constraint_value for the @platforms//os:os constraint_setting
+	platformOsMap = map[string]string{
+		osAndroid:         "//build/bazel/platforms/os:android",
+		osDarwin:          "//build/bazel/platforms/os:darwin",
+		osFuchsia:         "//build/bazel/platforms/os:fuchsia",
+		osLinux:           "//build/bazel/platforms/os:linux",
+		osLinuxBionic:     "//build/bazel/platforms/os:linux_bionic",
+		osWindows:         "//build/bazel/platforms/os:windows",
+		conditionsDefault: ConditionsDefaultSelectKey, // The default condition of an os select map.
+	}
+
+	platformOsArchMap = map[string]string{
+		osArchAndroidArm:        "//build/bazel/platforms/os_arch:android_arm",
+		osArchAndroidArm64:      "//build/bazel/platforms/os_arch:android_arm64",
+		osArchAndroidX86:        "//build/bazel/platforms/os_arch:android_x86",
+		osArchAndroidX86_64:     "//build/bazel/platforms/os_arch:android_x86_64",
+		osArchDarwinX86_64:      "//build/bazel/platforms/os_arch:darwin_x86_64",
+		osArchFuchsiaArm64:      "//build/bazel/platforms/os_arch:fuchsia_arm64",
+		osArchFuchsiaX86_64:     "//build/bazel/platforms/os_arch:fuchsia_x86_64",
+		osArchLinuxX86:          "//build/bazel/platforms/os_arch:linux_glibc_x86",
+		osArchLinuxX86_64:       "//build/bazel/platforms/os_arch:linux_glibc_x86_64",
+		osArchLinuxBionicArm64:  "//build/bazel/platforms/os_arch:linux_bionic_arm64",
+		osArchLinuxBionicX86_64: "//build/bazel/platforms/os_arch:linux_bionic_x86_64",
+		osArchWindowsX86:        "//build/bazel/platforms/os_arch:windows_x86",
+		osArchWindowsX86_64:     "//build/bazel/platforms/os_arch:windows_x86_64",
+		conditionsDefault:       ConditionsDefaultSelectKey, // The default condition of an os select map.
+	}
+)
+
+// basic configuration types
+type configurationType int
+
+const (
+	noConfig configurationType = iota
+	arch
+	os
+	osArch
+	productVariables
+)
+
+func (ct configurationType) String() string {
+	return map[configurationType]string{
+		noConfig:         "no_config",
+		arch:             "arch",
+		os:               "os",
+		osArch:           "arch_os",
+		productVariables: "product_variables",
+	}[ct]
+}
+
+func (ct configurationType) validateConfig(config string) {
+	switch ct {
+	case noConfig:
+		if config != "" {
+			panic(fmt.Errorf("Cannot specify config with %s, but got %s", ct, config))
+		}
+	case arch:
+		if _, ok := platformArchMap[config]; !ok {
+			panic(fmt.Errorf("Unknown arch: %s", config))
+		}
+	case os:
+		if _, ok := platformOsMap[config]; !ok {
+			panic(fmt.Errorf("Unknown os: %s", config))
+		}
+	case osArch:
+		if _, ok := platformOsArchMap[config]; !ok {
+			panic(fmt.Errorf("Unknown os+arch: %s", config))
+		}
+	case productVariables:
+		// do nothing
+	default:
+		panic(fmt.Errorf("Unrecognized ConfigurationType %d", ct))
+	}
+}
+
+// SelectKey returns the Bazel select key for a given configurationType and config string.
+func (ct configurationType) SelectKey(config string) string {
+	ct.validateConfig(config)
+	switch ct {
+	case noConfig:
+		panic(fmt.Errorf("SelectKey is unnecessary for noConfig ConfigurationType "))
+	case arch:
+		return platformArchMap[config]
+	case os:
+		return platformOsMap[config]
+	case osArch:
+		return platformOsArchMap[config]
+	case productVariables:
+		if config == conditionsDefault {
+			return ConditionsDefaultSelectKey
+		}
+		return fmt.Sprintf("%s:%s", productVariableBazelPackage, strings.ToLower(config))
+	default:
+		panic(fmt.Errorf("Unrecognized ConfigurationType %d", ct))
+	}
+}
+
+var (
+	// Indicating there is no configuration axis
+	NoConfigAxis = ConfigurationAxis{configurationType: noConfig}
+	// An axis for architecture-specific configurations
+	ArchConfigurationAxis = ConfigurationAxis{configurationType: arch}
+	// An axis for os-specific configurations
+	OsConfigurationAxis = ConfigurationAxis{configurationType: os}
+	// An axis for arch+os-specific configurations
+	OsArchConfigurationAxis = ConfigurationAxis{configurationType: osArch}
+)
+
+// ProductVariableConfigurationAxis returns an axis for the given product variable
+func ProductVariableConfigurationAxis(variable string) ConfigurationAxis {
+	return ConfigurationAxis{
+		configurationType: productVariables,
+		subType:           variable,
+	}
+}
+
+// ConfigurationAxis is an independent axis for configuration, there should be no overlap between
+// elements within an axis.
+type ConfigurationAxis struct {
+	configurationType
+	// some configuration types (e.g. productVariables) have multiple independent axes, subType helps
+	// distinguish between them without needing to list all 17 product variables.
+	subType string
+}
+
+func (ca *ConfigurationAxis) less(other ConfigurationAxis) bool {
+	if ca.configurationType < other.configurationType {
+		return true
+	}
+	return ca.subType < other.subType
+}
diff --git a/bazel/cquery/request_type.go b/bazel/cquery/request_type.go
index c30abeb..92135c6 100644
--- a/bazel/cquery/request_type.go
+++ b/bazel/cquery/request_type.go
@@ -16,6 +16,10 @@
 	CcStaticLibraryFiles []string
 	Includes             []string
 	SystemIncludes       []string
+	// Archives owned by the current target (not by its dependencies). These will
+	// be a subset of OutputFiles. (or static libraries, this will be equal to OutputFiles,
+	// but general cc_library will also have dynamic libraries in output files).
+	RootStaticArchives []string
 }
 
 type getOutputFilesRequestType struct{}
@@ -70,6 +74,7 @@
 
 ccObjectFiles = []
 staticLibraries = []
+rootStaticArchives = []
 linker_inputs = providers(target)["CcInfo"].linking_context.linker_inputs.to_list()
 
 for linker_input in linker_inputs:
@@ -78,6 +83,8 @@
       ccObjectFiles += [object.path]
     if library.static_library:
       staticLibraries.append(library.static_library.path)
+      if linker_input.owner == target.label:
+        rootStaticArchives.append(library.static_library.path)
 
 returns = [
   outputFiles,
@@ -85,6 +92,7 @@
   ccObjectFiles,
   includes,
   system_includes,
+  rootStaticArchives
 ]
 
 return "|".join([", ".join(r) for r in returns])`
@@ -98,7 +106,7 @@
 	var ccObjects []string
 
 	splitString := strings.Split(rawString, "|")
-	if expectedLen := 5; len(splitString) != expectedLen {
+	if expectedLen := 6; len(splitString) != expectedLen {
 		return CcInfo{}, fmt.Errorf("Expected %d items, got %q", expectedLen, splitString)
 	}
 	outputFilesString := splitString[0]
@@ -109,12 +117,14 @@
 	ccObjects = splitOrEmpty(ccObjectsString, ", ")
 	includes := splitOrEmpty(splitString[3], ", ")
 	systemIncludes := splitOrEmpty(splitString[4], ", ")
+	rootStaticArchives := splitOrEmpty(splitString[5], ", ")
 	return CcInfo{
 		OutputFiles:          outputFiles,
 		CcObjectFiles:        ccObjects,
 		CcStaticLibraryFiles: ccStaticLibraries,
 		Includes:             includes,
 		SystemIncludes:       systemIncludes,
+		RootStaticArchives:   rootStaticArchives,
 	}, nil
 }
 
diff --git a/bazel/cquery/request_type_test.go b/bazel/cquery/request_type_test.go
index 6369999..602849e 100644
--- a/bazel/cquery/request_type_test.go
+++ b/bazel/cquery/request_type_test.go
@@ -46,48 +46,51 @@
 	}{
 		{
 			description: "no result",
-			input:       "||||",
+			input:       "|||||",
 			expectedOutput: CcInfo{
 				OutputFiles:          []string{},
 				CcObjectFiles:        []string{},
 				CcStaticLibraryFiles: []string{},
 				Includes:             []string{},
 				SystemIncludes:       []string{},
+				RootStaticArchives:   []string{},
 			},
 		},
 		{
 			description: "only output",
-			input:       "test||||",
+			input:       "test|||||",
 			expectedOutput: CcInfo{
 				OutputFiles:          []string{"test"},
 				CcObjectFiles:        []string{},
 				CcStaticLibraryFiles: []string{},
 				Includes:             []string{},
 				SystemIncludes:       []string{},
+				RootStaticArchives:   []string{},
 			},
 		},
 		{
 			description: "all items set",
-			input:       "out1, out2|static_lib1, static_lib2|object1, object2|., dir/subdir|system/dir, system/other/dir",
+			input:       "out1, out2|static_lib1, static_lib2|object1, object2|., dir/subdir|system/dir, system/other/dir|rootstaticarchive1",
 			expectedOutput: CcInfo{
 				OutputFiles:          []string{"out1", "out2"},
 				CcObjectFiles:        []string{"object1", "object2"},
 				CcStaticLibraryFiles: []string{"static_lib1", "static_lib2"},
 				Includes:             []string{".", "dir/subdir"},
 				SystemIncludes:       []string{"system/dir", "system/other/dir"},
+				RootStaticArchives:   []string{"rootstaticarchive1"},
 			},
 		},
 		{
 			description:          "too few result splits",
 			input:                "|",
 			expectedOutput:       CcInfo{},
-			expectedErrorMessage: fmt.Sprintf("Expected %d items, got %q", 5, []string{"", ""}),
+			expectedErrorMessage: fmt.Sprintf("Expected %d items, got %q", 6, []string{"", ""}),
 		},
 		{
 			description:          "too many result splits",
 			input:                strings.Repeat("|", 8),
 			expectedOutput:       CcInfo{},
-			expectedErrorMessage: fmt.Sprintf("Expected %d items, got %q", 5, make([]string, 9)),
+			expectedErrorMessage: fmt.Sprintf("Expected %d items, got %q", 6, make([]string, 9)),
 		},
 	}
 	for _, tc := range testCases {
diff --git a/bazel/properties.go b/bazel/properties.go
index 951081c..99119cd 100644
--- a/bazel/properties.go
+++ b/bazel/properties.go
@@ -19,7 +19,6 @@
 	"path/filepath"
 	"regexp"
 	"sort"
-	"strings"
 )
 
 // BazelTargetModuleProperties contain properties and metadata used for
@@ -65,6 +64,17 @@
 	Excludes []Label
 }
 
+func (ll *LabelList) IsNil() bool {
+	return ll.Includes == nil && ll.Excludes == nil
+}
+
+func (ll *LabelList) deepCopy() LabelList {
+	return LabelList{
+		Includes: ll.Includes[:],
+		Excludes: ll.Excludes[:],
+	}
+}
+
 // uniqueParentDirectories returns a list of the unique parent directories for
 // all files in ll.Includes.
 func (ll *LabelList) uniqueParentDirectories() []string {
@@ -106,7 +116,27 @@
 	return uniqueLabels
 }
 
-func UniqueBazelLabelList(originalLabelList LabelList) LabelList {
+func FirstUniqueBazelLabels(originalLabels []Label) []Label {
+	var labels []Label
+	found := make(map[Label]bool, len(originalLabels))
+	for _, l := range originalLabels {
+		if _, ok := found[l]; ok {
+			continue
+		}
+		labels = append(labels, l)
+		found[l] = true
+	}
+	return labels
+}
+
+func FirstUniqueBazelLabelList(originalLabelList LabelList) LabelList {
+	var uniqueLabelList LabelList
+	uniqueLabelList.Includes = FirstUniqueBazelLabels(originalLabelList.Includes)
+	uniqueLabelList.Excludes = FirstUniqueBazelLabels(originalLabelList.Excludes)
+	return uniqueLabelList
+}
+
+func UniqueSortedBazelLabelList(originalLabelList LabelList) LabelList {
 	var uniqueLabelList LabelList
 	uniqueLabelList.Includes = UniqueSortedBazelLabels(originalLabelList.Includes)
 	uniqueLabelList.Excludes = UniqueSortedBazelLabels(originalLabelList.Excludes)
@@ -154,16 +184,9 @@
 
 	result.Value = MapLabelList(mapOver.Value, mapFn)
 
-	for arch := range PlatformArchMap {
-		result.SetValueForArch(arch, MapLabelList(mapOver.GetValueForArch(arch), mapFn))
-	}
-
-	for os := range PlatformOsMap {
-		result.SetOsValueForTarget(os, MapLabelList(mapOver.GetOsValueForTarget(os), mapFn))
-
-		// TODO(b/187530594): Should we handle arch=CONDITIONS_DEFAULT here? (not in ArchValues)
-		for _, arch := range AllArches {
-			result.SetOsArchValueForTarget(os, arch, MapLabelList(mapOver.GetOsArchValueForTarget(os, arch), mapFn))
+	for axis, configToLabels := range mapOver.ConfigurableValues {
+		for config, value := range configToLabels {
+			result.SetSelectValue(axis, config, MapLabelList(value, mapFn))
 		}
 	}
 
@@ -184,21 +207,14 @@
 
 // Return all needles in a given haystack, where needleFn is true for needles.
 func FilterLabelListAttribute(haystack LabelListAttribute, needleFn func(string) bool) LabelListAttribute {
-	var result LabelListAttribute
+	result := MakeLabelListAttribute(FilterLabelList(haystack.Value, needleFn))
 
-	result.Value = FilterLabelList(haystack.Value, needleFn)
-
-	for arch := range PlatformArchMap {
-		result.SetValueForArch(arch, FilterLabelList(haystack.GetValueForArch(arch), needleFn))
-	}
-
-	for os := range PlatformOsMap {
-		result.SetOsValueForTarget(os, FilterLabelList(haystack.GetOsValueForTarget(os), needleFn))
-
-		// TODO(b/187530594): Should we handle arch=CONDITIONS_DEFAULT here? (not in ArchValues)
-		for _, arch := range AllArches {
-			result.SetOsArchValueForTarget(os, arch, FilterLabelList(haystack.GetOsArchValueForTarget(os, arch), needleFn))
+	for config, selects := range haystack.ConfigurableValues {
+		newSelects := make(labelListSelectValues, len(selects))
+		for k, v := range selects {
+			newSelects[k] = FilterLabelList(v, needleFn)
 		}
+		result.ConfigurableValues[config] = newSelects
 	}
 
 	return result
@@ -206,24 +222,18 @@
 
 // Subtract needle from haystack
 func SubtractBazelLabelListAttribute(haystack LabelListAttribute, needle LabelListAttribute) LabelListAttribute {
-	var result LabelListAttribute
+	result := MakeLabelListAttribute(SubtractBazelLabelList(haystack.Value, needle.Value))
 
-	for arch := range PlatformArchMap {
-		result.SetValueForArch(arch,
-			SubtractBazelLabelList(haystack.GetValueForArch(arch), needle.GetValueForArch(arch)))
-	}
+	for config, selects := range haystack.ConfigurableValues {
+		newSelects := make(labelListSelectValues, len(selects))
+		needleSelects := needle.ConfigurableValues[config]
 
-	for os := range PlatformOsMap {
-		result.SetOsValueForTarget(os, SubtractBazelLabelList(haystack.GetOsValueForTarget(os), needle.GetOsValueForTarget(os)))
-
-		// TODO(b/187530594): Should we handle arch=CONDITIONS_DEFAULT here? (not in ArchValues)
-		for _, arch := range AllArches {
-			result.SetOsArchValueForTarget(os, arch, SubtractBazelLabelList(haystack.GetOsArchValueForTarget(os, arch), needle.GetOsArchValueForTarget(os, arch)))
+		for k, v := range selects {
+			newSelects[k] = SubtractBazelLabelList(v, needleSelects[k])
 		}
+		result.ConfigurableValues[config] = newSelects
 	}
 
-	result.Value = SubtractBazelLabelList(haystack.Value, needle.Value)
-
 	return result
 }
 
@@ -268,489 +278,235 @@
 	return result
 }
 
-const (
-	// ArchType names in arch.go
-	ARCH_ARM    = "arm"
-	ARCH_ARM64  = "arm64"
-	ARCH_X86    = "x86"
-	ARCH_X86_64 = "x86_64"
-
-	// OsType names in arch.go
-	OS_ANDROID      = "android"
-	OS_DARWIN       = "darwin"
-	OS_FUCHSIA      = "fuchsia"
-	OS_LINUX        = "linux_glibc"
-	OS_LINUX_BIONIC = "linux_bionic"
-	OS_WINDOWS      = "windows"
-
-	// Targets in arch.go
-	TARGET_ANDROID_ARM         = "android_arm"
-	TARGET_ANDROID_ARM64       = "android_arm64"
-	TARGET_ANDROID_X86         = "android_x86"
-	TARGET_ANDROID_X86_64      = "android_x86_64"
-	TARGET_DARWIN_X86_64       = "darwin_x86_64"
-	TARGET_FUCHSIA_ARM64       = "fuchsia_arm64"
-	TARGET_FUCHSIA_X86_64      = "fuchsia_x86_64"
-	TARGET_LINUX_X86           = "linux_glibc_x86"
-	TARGET_LINUX_x86_64        = "linux_glibc_x86_64"
-	TARGET_LINUX_BIONIC_ARM64  = "linux_bionic_arm64"
-	TARGET_LINUX_BIONIC_X86_64 = "linux_bionic_x86_64"
-	TARGET_WINDOWS_X86         = "windows_x86"
-	TARGET_WINDOWS_X86_64      = "windows_x86_64"
-
-	// This is the string representation of the default condition wherever a
-	// configurable attribute is used in a select statement, i.e.
-	// //conditions:default for Bazel.
-	//
-	// This is consistently named "conditions_default" to mirror the Soong
-	// config variable default key in an Android.bp file, although there's no
-	// integration with Soong config variables (yet).
-	CONDITIONS_DEFAULT = "conditions_default"
-
-	ConditionsDefaultSelectKey = "//conditions:default"
-
-	productVariableBazelPackage = "//build/bazel/product_variables"
-)
-
-var (
-	// These are the list of OSes and architectures with a Bazel config_setting
-	// and constraint value equivalent. These exist in arch.go, but the android
-	// package depends on the bazel package, so a cyclic dependency prevents
-	// using those variables here.
-
-	// A map of architectures to the Bazel label of the constraint_value
-	// for the @platforms//cpu:cpu constraint_setting
-	PlatformArchMap = map[string]string{
-		ARCH_ARM:           "//build/bazel/platforms/arch:arm",
-		ARCH_ARM64:         "//build/bazel/platforms/arch:arm64",
-		ARCH_X86:           "//build/bazel/platforms/arch:x86",
-		ARCH_X86_64:        "//build/bazel/platforms/arch:x86_64",
-		CONDITIONS_DEFAULT: ConditionsDefaultSelectKey, // The default condition of as arch select map.
-	}
-
-	// A map of target operating systems to the Bazel label of the
-	// constraint_value for the @platforms//os:os constraint_setting
-	PlatformOsMap = map[string]string{
-		OS_ANDROID:         "//build/bazel/platforms/os:android",
-		OS_DARWIN:          "//build/bazel/platforms/os:darwin",
-		OS_FUCHSIA:         "//build/bazel/platforms/os:fuchsia",
-		OS_LINUX:           "//build/bazel/platforms/os:linux",
-		OS_LINUX_BIONIC:    "//build/bazel/platforms/os:linux_bionic",
-		OS_WINDOWS:         "//build/bazel/platforms/os:windows",
-		CONDITIONS_DEFAULT: ConditionsDefaultSelectKey, // The default condition of an os select map.
-	}
-
-	PlatformTargetMap = map[string]string{
-		TARGET_ANDROID_ARM:         "//build/bazel/platforms/os_arch:android_arm",
-		TARGET_ANDROID_ARM64:       "//build/bazel/platforms/os_arch:android_arm64",
-		TARGET_ANDROID_X86:         "//build/bazel/platforms/os_arch:android_x86",
-		TARGET_ANDROID_X86_64:      "//build/bazel/platforms/os_arch:android_x86_64",
-		TARGET_DARWIN_X86_64:       "//build/bazel/platforms/os_arch:darwin_x86_64",
-		TARGET_FUCHSIA_ARM64:       "//build/bazel/platforms/os_arch:fuchsia_arm64",
-		TARGET_FUCHSIA_X86_64:      "//build/bazel/platforms/os_arch:fuchsia_x86_64",
-		TARGET_LINUX_X86:           "//build/bazel/platforms/os_arch:linux_glibc_x86",
-		TARGET_LINUX_x86_64:        "//build/bazel/platforms/os_arch:linux_glibc_x86_64",
-		TARGET_LINUX_BIONIC_ARM64:  "//build/bazel/platforms/os_arch:linux_bionic_arm64",
-		TARGET_LINUX_BIONIC_X86_64: "//build/bazel/platforms/os_arch:linux_bionic_x86_64",
-		TARGET_WINDOWS_X86:         "//build/bazel/platforms/os_arch:windows_x86",
-		TARGET_WINDOWS_X86_64:      "//build/bazel/platforms/os_arch:windows_x86_64",
-		CONDITIONS_DEFAULT:         ConditionsDefaultSelectKey, // The default condition of an os select map.
-	}
-
-	// TODO(b/187530594): Should we add CONDITIONS_DEFAULT here?
-	AllArches = []string{ARCH_ARM, ARCH_ARM64, ARCH_X86, ARCH_X86_64}
-)
-
 type Attribute interface {
 	HasConfigurableValues() bool
 }
 
-type labelArchValues struct {
-	X86    Label
-	X86_64 Label
-	Arm    Label
-	Arm64  Label
+type labelSelectValues map[string]*Label
 
-	ConditionsDefault Label
-}
+type configurableLabels map[ConfigurationAxis]labelSelectValues
 
-type labelTargetValue struct {
-	// E.g. for android
-	OsValue Label
-
-	// E.g. for android_arm, android_arm64, ...
-	ArchValues labelArchValues
-}
-
-type labelTargetValues struct {
-	Android     labelTargetValue
-	Darwin      labelTargetValue
-	Fuchsia     labelTargetValue
-	Linux       labelTargetValue
-	LinuxBionic labelTargetValue
-	Windows     labelTargetValue
-
-	ConditionsDefault labelTargetValue
+func (cl configurableLabels) setValueForAxis(axis ConfigurationAxis, config string, value *Label) {
+	if cl[axis] == nil {
+		cl[axis] = make(labelSelectValues)
+	}
+	cl[axis][config] = value
 }
 
 // Represents an attribute whose value is a single label
 type LabelAttribute struct {
-	Value Label
+	Value *Label
 
-	ArchValues labelArchValues
-
-	TargetValues labelTargetValues
+	ConfigurableValues configurableLabels
 }
 
-func (attr *LabelAttribute) GetValueForArch(arch string) Label {
-	var v *Label
-	if v = attr.archValuePtrs()[arch]; v == nil {
-		panic(fmt.Errorf("Unknown arch: %s", arch))
-	}
-	return *v
+// HasConfigurableValues returns whether there are configurable values set for this label.
+func (la LabelAttribute) HasConfigurableValues() bool {
+	return len(la.ConfigurableValues) > 0
 }
 
-func (attr *LabelAttribute) SetValueForArch(arch string, value Label) {
-	var v *Label
-	if v = attr.archValuePtrs()[arch]; v == nil {
-		panic(fmt.Errorf("Unknown arch: %s", arch))
-	}
-	*v = value
+// SetValue sets the base, non-configured value for the Label
+func (la *LabelAttribute) SetValue(value Label) {
+	la.SetSelectValue(NoConfigAxis, "", value)
 }
 
-func (attr *LabelAttribute) archValuePtrs() map[string]*Label {
-	return map[string]*Label{
-		ARCH_X86:           &attr.ArchValues.X86,
-		ARCH_X86_64:        &attr.ArchValues.X86_64,
-		ARCH_ARM:           &attr.ArchValues.Arm,
-		ARCH_ARM64:         &attr.ArchValues.Arm64,
-		CONDITIONS_DEFAULT: &attr.ArchValues.ConditionsDefault,
-	}
-}
-
-func (attr LabelAttribute) HasConfigurableValues() bool {
-	for arch := range PlatformArchMap {
-		if attr.GetValueForArch(arch).Label != "" {
-			return true
+// SetSelectValue set a value for a bazel select for the given axis, config and value.
+func (la *LabelAttribute) SetSelectValue(axis ConfigurationAxis, config string, value Label) {
+	axis.validateConfig(config)
+	switch axis.configurationType {
+	case noConfig:
+		la.Value = &value
+	case arch, os, osArch, productVariables:
+		if la.ConfigurableValues == nil {
+			la.ConfigurableValues = make(configurableLabels)
 		}
+		la.ConfigurableValues.setValueForAxis(axis, config, &value)
+	default:
+		panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
+	}
+}
+
+// SelectValue gets a value for a bazel select for the given axis and config.
+func (la *LabelAttribute) SelectValue(axis ConfigurationAxis, config string) Label {
+	axis.validateConfig(config)
+	switch axis.configurationType {
+	case noConfig:
+		return *la.Value
+	case arch, os, osArch, productVariables:
+		return *la.ConfigurableValues[axis][config]
+	default:
+		panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
+	}
+}
+
+// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
+func (la *LabelAttribute) SortedConfigurationAxes() []ConfigurationAxis {
+	keys := make([]ConfigurationAxis, 0, len(la.ConfigurableValues))
+	for k := range la.ConfigurableValues {
+		keys = append(keys, k)
 	}
 
-	for os := range PlatformOsMap {
-		if attr.GetOsValueForTarget(os).Label != "" {
+	sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
+	return keys
+}
+
+// labelListSelectValues supports config-specific label_list typed Bazel attribute values.
+type labelListSelectValues map[string]LabelList
+
+func (ll labelListSelectValues) appendSelects(other labelListSelectValues) {
+	for k, v := range other {
+		l := ll[k]
+		(&l).Append(v)
+		ll[k] = l
+	}
+}
+
+// HasConfigurableValues returns whether there are configurable values within this set of selects.
+func (ll labelListSelectValues) HasConfigurableValues() bool {
+	for _, v := range ll {
+		if len(v.Includes) > 0 {
 			return true
 		}
-		// TODO(b/187530594): Should we also check arch=CONDITIONS_DEFAULT (not in AllArches)
-		for _, arch := range AllArches {
-			if attr.GetOsArchValueForTarget(os, arch).Label != "" {
-				return true
-			}
-		}
 	}
 	return false
 }
 
-func (attr *LabelAttribute) getValueForTarget(os string) labelTargetValue {
-	var v *labelTargetValue
-	if v = attr.targetValuePtrs()[os]; v == nil {
-		panic(fmt.Errorf("Unknown os: %s", os))
-	}
-	return *v
-}
-
-func (attr *LabelAttribute) GetOsValueForTarget(os string) Label {
-	var v *labelTargetValue
-	if v = attr.targetValuePtrs()[os]; v == nil {
-		panic(fmt.Errorf("Unknown os: %s", os))
-	}
-	return v.OsValue
-}
-
-func (attr *LabelAttribute) GetOsArchValueForTarget(os string, arch string) Label {
-	var v *labelTargetValue
-	if v = attr.targetValuePtrs()[os]; v == nil {
-		panic(fmt.Errorf("Unknown os: %s", os))
-	}
-	switch arch {
-	case ARCH_X86:
-		return v.ArchValues.X86
-	case ARCH_X86_64:
-		return v.ArchValues.X86_64
-	case ARCH_ARM:
-		return v.ArchValues.Arm
-	case ARCH_ARM64:
-		return v.ArchValues.Arm64
-	case CONDITIONS_DEFAULT:
-		return v.ArchValues.ConditionsDefault
-	default:
-		panic(fmt.Errorf("Unknown arch: %s\n", arch))
-	}
-}
-
-func (attr *LabelAttribute) setValueForTarget(os string, value labelTargetValue) {
-	var v *labelTargetValue
-	if v = attr.targetValuePtrs()[os]; v == nil {
-		panic(fmt.Errorf("Unknown os: %s", os))
-	}
-	*v = value
-}
-
-func (attr *LabelAttribute) SetOsValueForTarget(os string, value Label) {
-	var v *labelTargetValue
-	if v = attr.targetValuePtrs()[os]; v == nil {
-		panic(fmt.Errorf("Unknown os: %s", os))
-	}
-	v.OsValue = value
-}
-
-func (attr *LabelAttribute) SetOsArchValueForTarget(os string, arch string, value Label) {
-	var v *labelTargetValue
-	if v = attr.targetValuePtrs()[os]; v == nil {
-		panic(fmt.Errorf("Unknown os: %s", os))
-	}
-	switch arch {
-	case ARCH_X86:
-		v.ArchValues.X86 = value
-	case ARCH_X86_64:
-		v.ArchValues.X86_64 = value
-	case ARCH_ARM:
-		v.ArchValues.Arm = value
-	case ARCH_ARM64:
-		v.ArchValues.Arm64 = value
-	case CONDITIONS_DEFAULT:
-		v.ArchValues.ConditionsDefault = value
-	default:
-		panic(fmt.Errorf("Unknown arch: %s\n", arch))
-	}
-}
-
-func (attr *LabelAttribute) targetValuePtrs() map[string]*labelTargetValue {
-	return map[string]*labelTargetValue{
-		OS_ANDROID:         &attr.TargetValues.Android,
-		OS_DARWIN:          &attr.TargetValues.Darwin,
-		OS_FUCHSIA:         &attr.TargetValues.Fuchsia,
-		OS_LINUX:           &attr.TargetValues.Linux,
-		OS_LINUX_BIONIC:    &attr.TargetValues.LinuxBionic,
-		OS_WINDOWS:         &attr.TargetValues.Windows,
-		CONDITIONS_DEFAULT: &attr.TargetValues.ConditionsDefault,
-	}
-}
-
-// Arch-specific label_list typed Bazel attribute values. This should correspond
-// to the types of architectures supported for compilation in arch.go.
-type labelListArchValues struct {
-	X86    LabelList
-	X86_64 LabelList
-	Arm    LabelList
-	Arm64  LabelList
-
-	ConditionsDefault LabelList
-}
-
-type labelListTargetValue struct {
-	// E.g. for android
-	OsValue LabelList
-
-	// E.g. for android_arm, android_arm64, ...
-	ArchValues labelListArchValues
-}
-
-func (target *labelListTargetValue) Append(other labelListTargetValue) {
-	target.OsValue.Append(other.OsValue)
-	target.ArchValues.X86.Append(other.ArchValues.X86)
-	target.ArchValues.X86_64.Append(other.ArchValues.X86_64)
-	target.ArchValues.Arm.Append(other.ArchValues.Arm)
-	target.ArchValues.Arm64.Append(other.ArchValues.Arm64)
-	target.ArchValues.ConditionsDefault.Append(other.ArchValues.ConditionsDefault)
-}
-
-type labelListTargetValues struct {
-	Android     labelListTargetValue
-	Darwin      labelListTargetValue
-	Fuchsia     labelListTargetValue
-	Linux       labelListTargetValue
-	LinuxBionic labelListTargetValue
-	Windows     labelListTargetValue
-
-	ConditionsDefault labelListTargetValue
-}
-
 // LabelListAttribute is used to represent a list of Bazel labels as an
 // attribute.
 type LabelListAttribute struct {
-	// The non-arch specific attribute label list Value. Required.
+	// The non-configured attribute label list Value. Required.
 	Value LabelList
 
-	// The arch-specific attribute label list values. Optional. If used, these
-	// are generated in a select statement and appended to the non-arch specific
-	// label list Value.
-	ArchValues labelListArchValues
+	// The configured attribute label list Values. Optional
+	// a map of independent configurability axes
+	ConfigurableValues configurableLabelLists
+}
 
-	// The os-specific attribute label list values. Optional. If used, these
-	// are generated in a select statement and appended to the non-os specific
-	// label list Value.
-	TargetValues labelListTargetValues
+type configurableLabelLists map[ConfigurationAxis]labelListSelectValues
+
+func (cll configurableLabelLists) setValueForAxis(axis ConfigurationAxis, config string, list LabelList) {
+	if list.IsNil() {
+		if _, ok := cll[axis][config]; ok {
+			delete(cll[axis], config)
+		}
+		return
+	}
+	if cll[axis] == nil {
+		cll[axis] = make(labelListSelectValues)
+	}
+
+	cll[axis][config] = list
+}
+
+func (cll configurableLabelLists) Append(other configurableLabelLists) {
+	for axis, otherSelects := range other {
+		selects := cll[axis]
+		if selects == nil {
+			selects = make(labelListSelectValues, len(otherSelects))
+		}
+		selects.appendSelects(otherSelects)
+		cll[axis] = selects
+	}
 }
 
 // MakeLabelListAttribute initializes a LabelListAttribute with the non-arch specific value.
 func MakeLabelListAttribute(value LabelList) LabelListAttribute {
-	return LabelListAttribute{Value: UniqueBazelLabelList(value)}
+	return LabelListAttribute{
+		Value:              value,
+		ConfigurableValues: make(configurableLabelLists),
+	}
+}
+
+func (lla *LabelListAttribute) SetValue(list LabelList) {
+	lla.SetSelectValue(NoConfigAxis, "", list)
+}
+
+// SetSelectValue set a value for a bazel select for the given axis, config and value.
+func (lla *LabelListAttribute) SetSelectValue(axis ConfigurationAxis, config string, list LabelList) {
+	axis.validateConfig(config)
+	switch axis.configurationType {
+	case noConfig:
+		lla.Value = list
+	case arch, os, osArch, productVariables:
+		if lla.ConfigurableValues == nil {
+			lla.ConfigurableValues = make(configurableLabelLists)
+		}
+		lla.ConfigurableValues.setValueForAxis(axis, config, list)
+	default:
+		panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
+	}
+}
+
+// SelectValue gets a value for a bazel select for the given axis and config.
+func (lla *LabelListAttribute) SelectValue(axis ConfigurationAxis, config string) LabelList {
+	axis.validateConfig(config)
+	switch axis.configurationType {
+	case noConfig:
+		return lla.Value
+	case arch, os, osArch, productVariables:
+		return lla.ConfigurableValues[axis][config]
+	default:
+		panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
+	}
+}
+
+// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
+func (lla *LabelListAttribute) SortedConfigurationAxes() []ConfigurationAxis {
+	keys := make([]ConfigurationAxis, 0, len(lla.ConfigurableValues))
+	for k := range lla.ConfigurableValues {
+		keys = append(keys, k)
+	}
+
+	sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
+	return keys
 }
 
 // Append all values, including os and arch specific ones, from another
 // LabelListAttribute to this LabelListAttribute.
-func (attrs *LabelListAttribute) Append(other LabelListAttribute) {
-	for arch := range PlatformArchMap {
-		this := attrs.GetValueForArch(arch)
-		that := other.GetValueForArch(arch)
-		this.Append(that)
-		attrs.SetValueForArch(arch, this)
+func (lla *LabelListAttribute) Append(other LabelListAttribute) {
+	lla.Value.Append(other.Value)
+	if lla.ConfigurableValues == nil {
+		lla.ConfigurableValues = make(configurableLabelLists)
 	}
-
-	for os := range PlatformOsMap {
-		this := attrs.getValueForTarget(os)
-		that := other.getValueForTarget(os)
-		this.Append(that)
-		attrs.setValueForTarget(os, this)
-	}
-
-	attrs.Value.Append(other.Value)
+	lla.ConfigurableValues.Append(other.ConfigurableValues)
 }
 
-// HasArchSpecificValues returns true if the attribute contains
-// architecture-specific label_list values.
-func (attrs LabelListAttribute) HasConfigurableValues() bool {
-	for arch := range PlatformArchMap {
-		if len(attrs.GetValueForArch(arch).Includes) > 0 {
-			return true
+// HasConfigurableValues returns true if the attribute contains axis-specific label list values.
+func (lla LabelListAttribute) HasConfigurableValues() bool {
+	return len(lla.ConfigurableValues) > 0
+}
+
+// ResolveExcludes handles excludes across the various axes, ensuring that items are removed from
+// the base value and included in default values as appropriate.
+func (lla *LabelListAttribute) ResolveExcludes() {
+	for axis, configToLabels := range lla.ConfigurableValues {
+		baseLabels := lla.Value.deepCopy()
+		for config, val := range configToLabels {
+			// Exclude config-specific excludes from base value
+			lla.Value = SubtractBazelLabelList(lla.Value, LabelList{Includes: val.Excludes})
+
+			// add base values to config specific to add labels excluded by others in this axis
+			// then remove all config-specific excludes
+			allLabels := baseLabels.deepCopy()
+			allLabels.Append(val)
+			lla.ConfigurableValues[axis][config] = SubtractBazelLabelList(allLabels, LabelList{Includes: val.Excludes})
 		}
-	}
 
-	for os := range PlatformOsMap {
-		if len(attrs.GetOsValueForTarget(os).Includes) > 0 {
-			return true
+		// After going through all configs, delete the duplicates in the config
+		// values that are already in the base Value.
+		for config, val := range configToLabels {
+			lla.ConfigurableValues[axis][config] = SubtractBazelLabelList(val, lla.Value)
 		}
-		// TODO(b/187530594): Should we also check arch=CONDITIONS_DEFAULT (not in AllArches)
-		for _, arch := range AllArches {
-			if len(attrs.GetOsArchValueForTarget(os, arch).Includes) > 0 {
-				return true
-			}
+
+		// Now that the Value list is finalized for this axis, compare it with the original
+		// list, and put the difference into the default condition for the axis.
+		lla.ConfigurableValues[axis][conditionsDefault] = SubtractBazelLabelList(baseLabels, lla.Value)
+
+		// if everything ends up without includes, just delete the axis
+		if !lla.ConfigurableValues[axis].HasConfigurableValues() {
+			delete(lla.ConfigurableValues, axis)
 		}
 	}
-	return false
-}
-
-func (attrs *LabelListAttribute) archValuePtrs() map[string]*LabelList {
-	return map[string]*LabelList{
-		ARCH_X86:           &attrs.ArchValues.X86,
-		ARCH_X86_64:        &attrs.ArchValues.X86_64,
-		ARCH_ARM:           &attrs.ArchValues.Arm,
-		ARCH_ARM64:         &attrs.ArchValues.Arm64,
-		CONDITIONS_DEFAULT: &attrs.ArchValues.ConditionsDefault,
-	}
-}
-
-// GetValueForArch returns the label_list attribute value for an architecture.
-func (attrs *LabelListAttribute) GetValueForArch(arch string) LabelList {
-	var v *LabelList
-	if v = attrs.archValuePtrs()[arch]; v == nil {
-		panic(fmt.Errorf("Unknown arch: %s", arch))
-	}
-	return *v
-}
-
-// SetValueForArch sets the label_list attribute value for an architecture.
-func (attrs *LabelListAttribute) SetValueForArch(arch string, value LabelList) {
-	var v *LabelList
-	if v = attrs.archValuePtrs()[arch]; v == nil {
-		panic(fmt.Errorf("Unknown arch: %s", arch))
-	}
-	*v = value
-}
-
-func (attrs *LabelListAttribute) targetValuePtrs() map[string]*labelListTargetValue {
-	return map[string]*labelListTargetValue{
-		OS_ANDROID:         &attrs.TargetValues.Android,
-		OS_DARWIN:          &attrs.TargetValues.Darwin,
-		OS_FUCHSIA:         &attrs.TargetValues.Fuchsia,
-		OS_LINUX:           &attrs.TargetValues.Linux,
-		OS_LINUX_BIONIC:    &attrs.TargetValues.LinuxBionic,
-		OS_WINDOWS:         &attrs.TargetValues.Windows,
-		CONDITIONS_DEFAULT: &attrs.TargetValues.ConditionsDefault,
-	}
-}
-
-func (attrs *LabelListAttribute) getValueForTarget(os string) labelListTargetValue {
-	var v *labelListTargetValue
-	if v = attrs.targetValuePtrs()[os]; v == nil {
-		panic(fmt.Errorf("Unknown os: %s", os))
-	}
-	return *v
-}
-
-func (attrs *LabelListAttribute) GetOsValueForTarget(os string) LabelList {
-	var v *labelListTargetValue
-	if v = attrs.targetValuePtrs()[os]; v == nil {
-		panic(fmt.Errorf("Unknown os: %s", os))
-	}
-	return v.OsValue
-}
-
-func (attrs *LabelListAttribute) GetOsArchValueForTarget(os string, arch string) LabelList {
-	var v *labelListTargetValue
-	if v = attrs.targetValuePtrs()[os]; v == nil {
-		panic(fmt.Errorf("Unknown os: %s", os))
-	}
-	switch arch {
-	case ARCH_X86:
-		return v.ArchValues.X86
-	case ARCH_X86_64:
-		return v.ArchValues.X86_64
-	case ARCH_ARM:
-		return v.ArchValues.Arm
-	case ARCH_ARM64:
-		return v.ArchValues.Arm64
-	case CONDITIONS_DEFAULT:
-		return v.ArchValues.ConditionsDefault
-	default:
-		panic(fmt.Errorf("Unknown arch: %s\n", arch))
-	}
-}
-
-func (attrs *LabelListAttribute) setValueForTarget(os string, value labelListTargetValue) {
-	var v *labelListTargetValue
-	if v = attrs.targetValuePtrs()[os]; v == nil {
-		panic(fmt.Errorf("Unknown os: %s", os))
-	}
-	*v = value
-}
-
-func (attrs *LabelListAttribute) SetOsValueForTarget(os string, value LabelList) {
-	var v *labelListTargetValue
-	if v = attrs.targetValuePtrs()[os]; v == nil {
-		panic(fmt.Errorf("Unknown os: %s", os))
-	}
-	v.OsValue = value
-}
-
-func (attrs *LabelListAttribute) SetOsArchValueForTarget(os string, arch string, value LabelList) {
-	var v *labelListTargetValue
-	if v = attrs.targetValuePtrs()[os]; v == nil {
-		panic(fmt.Errorf("Unknown os: %s", os))
-	}
-	switch arch {
-	case ARCH_X86:
-		v.ArchValues.X86 = value
-	case ARCH_X86_64:
-		v.ArchValues.X86_64 = value
-	case ARCH_ARM:
-		v.ArchValues.Arm = value
-	case ARCH_ARM64:
-		v.ArchValues.Arm64 = value
-	case CONDITIONS_DEFAULT:
-		v.ArchValues.ConditionsDefault = value
-	default:
-		panic(fmt.Errorf("Unknown arch: %s\n", arch))
-	}
 }
 
 // StringListAttribute corresponds to the string_list Bazel attribute type with
@@ -759,256 +515,110 @@
 	// The base value of the string list attribute.
 	Value []string
 
-	// The arch-specific attribute string list values. Optional. If used, these
-	// are generated in a select statement and appended to the non-arch specific
-	// label list Value.
-	ArchValues stringListArchValues
+	// The configured attribute label list Values. Optional
+	// a map of independent configurability axes
+	ConfigurableValues configurableStringLists
+}
 
-	// The os-specific attribute string list values. Optional. If used, these
-	// are generated in a select statement and appended to the non-os specific
-	// label list Value.
-	TargetValues stringListTargetValues
+type configurableStringLists map[ConfigurationAxis]stringListSelectValues
 
-	// list of product-variable string list values. Optional. if used, each will generate a select
-	// statement appended to the label list Value.
-	ProductValues []ProductVariableValues
+func (csl configurableStringLists) Append(other configurableStringLists) {
+	for axis, otherSelects := range other {
+		selects := csl[axis]
+		if selects == nil {
+			selects = make(stringListSelectValues, len(otherSelects))
+		}
+		selects.appendSelects(otherSelects)
+		csl[axis] = selects
+	}
+}
+
+func (csl configurableStringLists) setValueForAxis(axis ConfigurationAxis, config string, list []string) {
+	if csl[axis] == nil {
+		csl[axis] = make(stringListSelectValues)
+	}
+	csl[axis][config] = list
+}
+
+type stringListSelectValues map[string][]string
+
+func (sl stringListSelectValues) appendSelects(other stringListSelectValues) {
+	for k, v := range other {
+		sl[k] = append(sl[k], v...)
+	}
+}
+
+func (sl stringListSelectValues) hasConfigurableValues(other stringListSelectValues) bool {
+	for _, val := range sl {
+		if len(val) > 0 {
+			return true
+		}
+	}
+	return false
 }
 
 // MakeStringListAttribute initializes a StringListAttribute with the non-arch specific value.
 func MakeStringListAttribute(value []string) StringListAttribute {
 	// NOTE: These strings are not necessarily unique or sorted.
-	return StringListAttribute{Value: value}
-}
-
-// Arch-specific string_list typed Bazel attribute values. This should correspond
-// to the types of architectures supported for compilation in arch.go.
-type stringListArchValues struct {
-	X86    []string
-	X86_64 []string
-	Arm    []string
-	Arm64  []string
-
-	ConditionsDefault []string
-}
-
-type stringListTargetValue struct {
-	// E.g. for android
-	OsValue []string
-
-	// E.g. for android_arm, android_arm64, ...
-	ArchValues stringListArchValues
-}
-
-func (target *stringListTargetValue) Append(other stringListTargetValue) {
-	target.OsValue = append(target.OsValue, other.OsValue...)
-	target.ArchValues.X86 = append(target.ArchValues.X86, other.ArchValues.X86...)
-	target.ArchValues.X86_64 = append(target.ArchValues.X86_64, other.ArchValues.X86_64...)
-	target.ArchValues.Arm = append(target.ArchValues.Arm, other.ArchValues.Arm...)
-	target.ArchValues.Arm64 = append(target.ArchValues.Arm64, other.ArchValues.Arm64...)
-	target.ArchValues.ConditionsDefault = append(target.ArchValues.ConditionsDefault, other.ArchValues.ConditionsDefault...)
-}
-
-type stringListTargetValues struct {
-	Android     stringListTargetValue
-	Darwin      stringListTargetValue
-	Fuchsia     stringListTargetValue
-	Linux       stringListTargetValue
-	LinuxBionic stringListTargetValue
-	Windows     stringListTargetValue
-
-	ConditionsDefault stringListTargetValue
-}
-
-// Product Variable values for StringListAttribute
-type ProductVariableValues struct {
-	ProductVariable string
-
-	Values []string
-}
-
-// SelectKey returns the appropriate select key for the receiving ProductVariableValues.
-func (p ProductVariableValues) SelectKey() string {
-	return fmt.Sprintf("%s:%s", productVariableBazelPackage, strings.ToLower(p.ProductVariable))
-}
-
-// HasConfigurableValues returns true if the attribute contains
-// architecture-specific string_list values.
-func (attrs StringListAttribute) HasConfigurableValues() bool {
-	for arch := range PlatformArchMap {
-		if len(attrs.GetValueForArch(arch)) > 0 {
-			return true
-		}
-	}
-
-	for os := range PlatformOsMap {
-		if len(attrs.GetOsValueForTarget(os)) > 0 {
-			return true
-		}
-		// TODO(b/187530594): Should we also check arch=CONDITIONS_DEFAULT? (Not in AllArches)
-		for _, arch := range AllArches {
-			if len(attrs.GetOsArchValueForTarget(os, arch)) > 0 {
-				return true
-			}
-
-		}
-	}
-
-	return len(attrs.ProductValues) > 0
-}
-
-func (attrs *StringListAttribute) archValuePtrs() map[string]*[]string {
-	return map[string]*[]string{
-		ARCH_X86:           &attrs.ArchValues.X86,
-		ARCH_X86_64:        &attrs.ArchValues.X86_64,
-		ARCH_ARM:           &attrs.ArchValues.Arm,
-		ARCH_ARM64:         &attrs.ArchValues.Arm64,
-		CONDITIONS_DEFAULT: &attrs.ArchValues.ConditionsDefault,
+	return StringListAttribute{
+		Value:              value,
+		ConfigurableValues: make(configurableStringLists),
 	}
 }
 
-// GetValueForArch returns the string_list attribute value for an architecture.
-func (attrs *StringListAttribute) GetValueForArch(arch string) []string {
-	var v *[]string
-	if v = attrs.archValuePtrs()[arch]; v == nil {
-		panic(fmt.Errorf("Unknown arch: %s", arch))
-	}
-	return *v
-}
-
-// SetValueForArch sets the string_list attribute value for an architecture.
-func (attrs *StringListAttribute) SetValueForArch(arch string, value []string) {
-	var v *[]string
-	if v = attrs.archValuePtrs()[arch]; v == nil {
-		panic(fmt.Errorf("Unknown arch: %s", arch))
-	}
-	*v = value
-}
-
-func (attrs *StringListAttribute) targetValuePtrs() map[string]*stringListTargetValue {
-	return map[string]*stringListTargetValue{
-		OS_ANDROID:         &attrs.TargetValues.Android,
-		OS_DARWIN:          &attrs.TargetValues.Darwin,
-		OS_FUCHSIA:         &attrs.TargetValues.Fuchsia,
-		OS_LINUX:           &attrs.TargetValues.Linux,
-		OS_LINUX_BIONIC:    &attrs.TargetValues.LinuxBionic,
-		OS_WINDOWS:         &attrs.TargetValues.Windows,
-		CONDITIONS_DEFAULT: &attrs.TargetValues.ConditionsDefault,
-	}
-}
-
-func (attrs *StringListAttribute) getValueForTarget(os string) stringListTargetValue {
-	var v *stringListTargetValue
-	if v = attrs.targetValuePtrs()[os]; v == nil {
-		panic(fmt.Errorf("Unknown os: %s", os))
-	}
-	return *v
-}
-
-func (attrs *StringListAttribute) GetOsValueForTarget(os string) []string {
-	var v *stringListTargetValue
-	if v = attrs.targetValuePtrs()[os]; v == nil {
-		panic(fmt.Errorf("Unknown os: %s", os))
-	}
-	return v.OsValue
-}
-
-func (attrs *StringListAttribute) GetOsArchValueForTarget(os string, arch string) []string {
-	var v *stringListTargetValue
-	if v = attrs.targetValuePtrs()[os]; v == nil {
-		panic(fmt.Errorf("Unknown os: %s", os))
-	}
-	switch arch {
-	case ARCH_X86:
-		return v.ArchValues.X86
-	case ARCH_X86_64:
-		return v.ArchValues.X86_64
-	case ARCH_ARM:
-		return v.ArchValues.Arm
-	case ARCH_ARM64:
-		return v.ArchValues.Arm64
-	case CONDITIONS_DEFAULT:
-		return v.ArchValues.ConditionsDefault
-	default:
-		panic(fmt.Errorf("Unknown arch: %s\n", arch))
-	}
-}
-
-func (attrs *StringListAttribute) setValueForTarget(os string, value stringListTargetValue) {
-	var v *stringListTargetValue
-	if v = attrs.targetValuePtrs()[os]; v == nil {
-		panic(fmt.Errorf("Unknown os: %s", os))
-	}
-	*v = value
-}
-
-func (attrs *StringListAttribute) SortedProductVariables() []ProductVariableValues {
-	vals := attrs.ProductValues[:]
-	sort.Slice(vals, func(i, j int) bool { return vals[i].ProductVariable < vals[j].ProductVariable })
-	return vals
-}
-
-func (attrs *StringListAttribute) SetOsValueForTarget(os string, value []string) {
-	var v *stringListTargetValue
-	if v = attrs.targetValuePtrs()[os]; v == nil {
-		panic(fmt.Errorf("Unknown os: %s", os))
-	}
-	v.OsValue = value
-}
-
-func (attrs *StringListAttribute) SetOsArchValueForTarget(os string, arch string, value []string) {
-	var v *stringListTargetValue
-	if v = attrs.targetValuePtrs()[os]; v == nil {
-		panic(fmt.Errorf("Unknown os: %s", os))
-	}
-	switch arch {
-	case ARCH_X86:
-		v.ArchValues.X86 = value
-	case ARCH_X86_64:
-		v.ArchValues.X86_64 = value
-	case ARCH_ARM:
-		v.ArchValues.Arm = value
-	case ARCH_ARM64:
-		v.ArchValues.Arm64 = value
-	case CONDITIONS_DEFAULT:
-		v.ArchValues.ConditionsDefault = value
-	default:
-		panic(fmt.Errorf("Unknown arch: %s\n", arch))
-	}
+// HasConfigurableValues returns true if the attribute contains axis-specific string_list values.
+func (sla StringListAttribute) HasConfigurableValues() bool {
+	return len(sla.ConfigurableValues) > 0
 }
 
 // Append appends all values, including os and arch specific ones, from another
 // StringListAttribute to this StringListAttribute
-func (attrs *StringListAttribute) Append(other StringListAttribute) {
-	for arch := range PlatformArchMap {
-		this := attrs.GetValueForArch(arch)
-		that := other.GetValueForArch(arch)
-		this = append(this, that...)
-		attrs.SetValueForArch(arch, this)
+func (sla *StringListAttribute) Append(other StringListAttribute) {
+	sla.Value = append(sla.Value, other.Value...)
+	if sla.ConfigurableValues == nil {
+		sla.ConfigurableValues = make(configurableStringLists)
+	}
+	sla.ConfigurableValues.Append(other.ConfigurableValues)
+}
+
+// SetSelectValue set a value for a bazel select for the given axis, config and value.
+func (sla *StringListAttribute) SetSelectValue(axis ConfigurationAxis, config string, list []string) {
+	axis.validateConfig(config)
+	switch axis.configurationType {
+	case noConfig:
+		sla.Value = list
+	case arch, os, osArch, productVariables:
+		if sla.ConfigurableValues == nil {
+			sla.ConfigurableValues = make(configurableStringLists)
+		}
+		sla.ConfigurableValues.setValueForAxis(axis, config, list)
+	default:
+		panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
+	}
+}
+
+// SelectValue gets a value for a bazel select for the given axis and config.
+func (sla *StringListAttribute) SelectValue(axis ConfigurationAxis, config string) []string {
+	axis.validateConfig(config)
+	switch axis.configurationType {
+	case noConfig:
+		return sla.Value
+	case arch, os, osArch, productVariables:
+		return sla.ConfigurableValues[axis][config]
+	default:
+		panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
+	}
+}
+
+// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
+func (sla *StringListAttribute) SortedConfigurationAxes() []ConfigurationAxis {
+	keys := make([]ConfigurationAxis, 0, len(sla.ConfigurableValues))
+	for k := range sla.ConfigurableValues {
+		keys = append(keys, k)
 	}
 
-	for os := range PlatformOsMap {
-		this := attrs.getValueForTarget(os)
-		that := other.getValueForTarget(os)
-		this.Append(that)
-		attrs.setValueForTarget(os, this)
-	}
-
-	productValues := make(map[string][]string, 0)
-	for _, pv := range attrs.ProductValues {
-		productValues[pv.ProductVariable] = pv.Values
-	}
-	for _, pv := range other.ProductValues {
-		productValues[pv.ProductVariable] = append(productValues[pv.ProductVariable], pv.Values...)
-	}
-	attrs.ProductValues = make([]ProductVariableValues, 0, len(productValues))
-	for pv, vals := range productValues {
-		attrs.ProductValues = append(attrs.ProductValues, ProductVariableValues{
-			ProductVariable: pv,
-			Values:          vals,
-		})
-	}
-
-	attrs.Value = append(attrs.Value, other.Value...)
+	sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
+	return keys
 }
 
 // TryVariableSubstitution, replace string substitution formatting within each string in slice with
diff --git a/bazel/properties_test.go b/bazel/properties_test.go
index 229a4aa..9464245 100644
--- a/bazel/properties_test.go
+++ b/bazel/properties_test.go
@@ -122,7 +122,7 @@
 		}
 	}
 }
-func TestUniqueBazelLabelList(t *testing.T) {
+func TestFirstUniqueBazelLabelList(t *testing.T) {
 	testCases := []struct {
 		originalLabelList       LabelList
 		expectedUniqueLabelList LabelList
@@ -157,9 +157,139 @@
 		},
 	}
 	for _, tc := range testCases {
-		actualUniqueLabelList := UniqueBazelLabelList(tc.originalLabelList)
+		actualUniqueLabelList := FirstUniqueBazelLabelList(tc.originalLabelList)
 		if !reflect.DeepEqual(tc.expectedUniqueLabelList, actualUniqueLabelList) {
 			t.Fatalf("Expected %v, got %v", tc.expectedUniqueLabelList, actualUniqueLabelList)
 		}
 	}
 }
+
+func TestUniqueSortedBazelLabelList(t *testing.T) {
+	testCases := []struct {
+		originalLabelList       LabelList
+		expectedUniqueLabelList LabelList
+	}{
+		{
+			originalLabelList: LabelList{
+				Includes: []Label{
+					{Label: "c"},
+					{Label: "a"},
+					{Label: "a"},
+					{Label: "b"},
+				},
+				Excludes: []Label{
+					{Label: "y"},
+					{Label: "z"},
+					{Label: "x"},
+					{Label: "x"},
+				},
+			},
+			expectedUniqueLabelList: LabelList{
+				Includes: []Label{
+					{Label: "a"},
+					{Label: "b"},
+					{Label: "c"},
+				},
+				Excludes: []Label{
+					{Label: "x"},
+					{Label: "y"},
+					{Label: "z"},
+				},
+			},
+		},
+	}
+	for _, tc := range testCases {
+		actualUniqueLabelList := UniqueSortedBazelLabelList(tc.originalLabelList)
+		if !reflect.DeepEqual(tc.expectedUniqueLabelList, actualUniqueLabelList) {
+			t.Fatalf("Expected %v, got %v", tc.expectedUniqueLabelList, actualUniqueLabelList)
+		}
+	}
+}
+
+func makeLabels(labels ...string) []Label {
+	var ret []Label
+	for _, l := range labels {
+		ret = append(ret, Label{Label: l})
+	}
+	return ret
+}
+
+func makeLabelList(includes, excludes []string) LabelList {
+	return LabelList{
+		Includes: makeLabels(includes...),
+		Excludes: makeLabels(excludes...),
+	}
+}
+
+func TestResolveExcludes(t *testing.T) {
+	attr := LabelListAttribute{
+		Value: makeLabelList(
+			[]string{
+				"all_include",
+				"arm_exclude",
+				"android_exclude",
+			},
+			[]string{"all_exclude"},
+		),
+		ConfigurableValues: configurableLabelLists{
+			ArchConfigurationAxis: labelListSelectValues{
+				"arm": makeLabelList([]string{}, []string{"arm_exclude"}),
+				"x86": makeLabelList([]string{"x86_include"}, []string{}),
+			},
+			OsConfigurationAxis: labelListSelectValues{
+				"android": makeLabelList([]string{}, []string{"android_exclude"}),
+				"linux":   makeLabelList([]string{"linux_include"}, []string{}),
+			},
+			OsArchConfigurationAxis: labelListSelectValues{
+				"linux_x86": makeLabelList([]string{"linux_x86_include"}, []string{}),
+			},
+			ProductVariableConfigurationAxis("a"): labelListSelectValues{
+				"a": makeLabelList([]string{}, []string{"not_in_value"}),
+			},
+		},
+	}
+
+	attr.ResolveExcludes()
+
+	expectedBaseIncludes := []Label{Label{Label: "all_include"}}
+	if !reflect.DeepEqual(expectedBaseIncludes, attr.Value.Includes) {
+		t.Errorf("Expected Value includes %q, got %q", attr.Value.Includes, expectedBaseIncludes)
+	}
+	var nilLabels []Label
+	expectedConfiguredIncludes := map[ConfigurationAxis]map[string][]Label{
+		ArchConfigurationAxis: map[string][]Label{
+			"arm":                nilLabels,
+			"x86":                makeLabels("arm_exclude", "x86_include"),
+			"conditions_default": makeLabels("arm_exclude"),
+		},
+		OsConfigurationAxis: map[string][]Label{
+			"android":            nilLabels,
+			"linux":              makeLabels("android_exclude", "linux_include"),
+			"conditions_default": makeLabels("android_exclude"),
+		},
+		OsArchConfigurationAxis: map[string][]Label{
+			"linux_x86":          makeLabels("linux_x86_include"),
+			"conditions_default": nilLabels,
+		},
+	}
+	for _, axis := range attr.SortedConfigurationAxes() {
+		if _, ok := expectedConfiguredIncludes[axis]; !ok {
+			t.Errorf("Found unexpected axis %s", axis)
+			continue
+		}
+		expectedForAxis := expectedConfiguredIncludes[axis]
+		gotForAxis := attr.ConfigurableValues[axis]
+		if len(expectedForAxis) != len(gotForAxis) {
+			t.Errorf("Expected %d configs for %s, got %d: %s", len(expectedForAxis), axis, len(gotForAxis), gotForAxis)
+		}
+		for config, value := range gotForAxis {
+			if expected, ok := expectedForAxis[config]; ok {
+				if !reflect.DeepEqual(expected, value.Includes) {
+					t.Errorf("For %s, expected: %#v, got %#v", axis, expected, value.Includes)
+				}
+			} else {
+				t.Errorf("Got unexpected config %q for %s", config, axis)
+			}
+		}
+	}
+}
diff --git a/bp2build/build_conversion_test.go b/bp2build/build_conversion_test.go
index b1c342c..6d0a9b2 100644
--- a/bp2build/build_conversion_test.go
+++ b/bp2build/build_conversion_test.go
@@ -1109,8 +1109,8 @@
         "out",
     ],
     srcs = [
-        "in1",
         "srcs-from-3",
+        "in1",
     ],
 )`,
 			description: "genrule applies properties from genrule_defaults transitively",
@@ -1535,10 +1535,10 @@
 			expectedBazelTargets: []string{`filegroup(
     name = "fg_foo",
     srcs = [
-        "//dir:e.txt",
-        "//dir:f.txt",
         "a.txt",
         "b.txt",
+        "//dir:e.txt",
+        "//dir:f.txt",
     ],
 )`,
 			},
@@ -1575,9 +1575,9 @@
 			expectedBazelTargets: []string{`filegroup(
     name = "fg_foo",
     srcs = [
+        "a.txt",
         "//dir/subdir:e.txt",
         "//dir/subdir:f.txt",
-        "a.txt",
     ],
 )`,
 			},
diff --git a/bp2build/cc_library_conversion_test.go b/bp2build/cc_library_conversion_test.go
index c8ae031..d84a7bb 100644
--- a/bp2build/cc_library_conversion_test.go
+++ b/bp2build/cc_library_conversion_test.go
@@ -985,3 +985,117 @@
 )`},
 	})
 }
+
+func TestCcLibraryExcludeLibs(t *testing.T) {
+	runCcLibraryTestCase(t, bp2buildTestCase{
+		moduleTypeUnderTest:                "cc_library",
+		moduleTypeUnderTestFactory:         cc.LibraryFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+		depsMutators:                       []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build},
+		filesystem:                         map[string]string{},
+		blueprint: soongCcLibraryStaticPreamble + `
+cc_library {
+    name: "foo_static",
+    srcs: ["common.c"],
+    whole_static_libs: [
+        "arm_whole_static_lib_excludes",
+        "malloc_not_svelte_whole_static_lib_excludes"
+    ],
+    static_libs: [
+        "arm_static_lib_excludes",
+        "malloc_not_svelte_static_lib_excludes"
+    ],
+    shared_libs: [
+        "arm_shared_lib_excludes",
+    ],
+    arch: {
+        arm: {
+            exclude_shared_libs: [
+                 "arm_shared_lib_excludes",
+            ],
+            exclude_static_libs: [
+                "arm_static_lib_excludes",
+                "arm_whole_static_lib_excludes",
+            ],
+        },
+    },
+    product_variables: {
+        malloc_not_svelte: {
+            shared_libs: ["malloc_not_svelte_shared_lib"],
+            whole_static_libs: ["malloc_not_svelte_whole_static_lib"],
+            exclude_static_libs: [
+                "malloc_not_svelte_static_lib_excludes",
+                "malloc_not_svelte_whole_static_lib_excludes",
+            ],
+        },
+    },
+}
+
+cc_library {
+    name: "arm_whole_static_lib_excludes",
+    bazel_module: { bp2build_available: false },
+}
+
+cc_library {
+    name: "malloc_not_svelte_whole_static_lib",
+    bazel_module: { bp2build_available: false },
+}
+
+cc_library {
+    name: "malloc_not_svelte_whole_static_lib_excludes",
+    bazel_module: { bp2build_available: false },
+}
+
+cc_library {
+    name: "arm_static_lib_excludes",
+    bazel_module: { bp2build_available: false },
+}
+
+cc_library {
+    name: "malloc_not_svelte_static_lib_excludes",
+    bazel_module: { bp2build_available: false },
+}
+
+cc_library {
+    name: "arm_shared_lib_excludes",
+    bazel_module: { bp2build_available: false },
+}
+
+cc_library {
+    name: "malloc_not_svelte_shared_lib",
+    bazel_module: { bp2build_available: false },
+}
+`,
+		expectedBazelTargets: []string{
+			`cc_library(
+    name = "foo_static",
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
+    dynamic_deps = select({
+        "//build/bazel/platforms/arch:arm": [],
+        "//conditions:default": [":arm_shared_lib_excludes"],
+    }) + select({
+        "//build/bazel/product_variables:malloc_not_svelte": [":malloc_not_svelte_shared_lib"],
+        "//conditions:default": [],
+    }),
+    implementation_deps = select({
+        "//build/bazel/platforms/arch:arm": [],
+        "//conditions:default": [":arm_static_lib_excludes"],
+    }) + select({
+        "//build/bazel/product_variables:malloc_not_svelte": [],
+        "//conditions:default": [":malloc_not_svelte_static_lib_excludes"],
+    }),
+    srcs_c = ["common.c"],
+    whole_archive_deps = select({
+        "//build/bazel/platforms/arch:arm": [],
+        "//conditions:default": [":arm_whole_static_lib_excludes"],
+    }) + select({
+        "//build/bazel/product_variables:malloc_not_svelte": [":malloc_not_svelte_whole_static_lib"],
+        "//conditions:default": [":malloc_not_svelte_whole_static_lib_excludes"],
+    }),
+)`,
+		},
+	})
+}
diff --git a/bp2build/cc_library_static_conversion_test.go b/bp2build/cc_library_static_conversion_test.go
index da38adb..40edec8 100644
--- a/bp2build/cc_library_static_conversion_test.go
+++ b/bp2build/cc_library_static_conversion_test.go
@@ -1286,8 +1286,6 @@
         "//build/bazel/platforms/os_arch:android_arm64": ["android_arm64_src.c"],
         "//build/bazel/platforms/os_arch:android_x86": ["android_x86_src.c"],
         "//build/bazel/platforms/os_arch:android_x86_64": ["android_x86_64_src.c"],
-        "//conditions:default": [],
-    }) + select({
         "//build/bazel/platforms/os_arch:linux_bionic_arm64": ["linux_bionic_arm64_src.c"],
         "//build/bazel/platforms/os_arch:linux_bionic_x86_64": ["linux_bionic_x86_64_src.c"],
         "//conditions:default": [],
diff --git a/bp2build/configurability.go b/bp2build/configurability.go
index 7e1a298..60f6330 100644
--- a/bp2build/configurability.go
+++ b/bp2build/configurability.go
@@ -17,46 +17,20 @@
 		return value, []selects{}
 	}
 
-	selectValues := make([]selects, 0)
-	archSelects := map[string]reflect.Value{}
-	for arch, selectKey := range bazel.PlatformArchMap {
-		archSelects[selectKey] = reflect.ValueOf(list.GetValueForArch(arch))
-	}
-	if len(archSelects) > 0 {
-		selectValues = append(selectValues, archSelects)
-	}
-
-	osSelects := map[string]reflect.Value{}
-	osArchSelects := make([]selects, 0)
-	for _, os := range android.SortedStringKeys(bazel.PlatformOsMap) {
-		selectKey := bazel.PlatformOsMap[os]
-		osSelects[selectKey] = reflect.ValueOf(list.GetOsValueForTarget(os))
-		archSelects := make(map[string]reflect.Value)
-		// TODO(b/187530594): Should we also check arch=CONDITIONS_DEFAULT? (not in AllArches)
-		for _, arch := range bazel.AllArches {
-			target := os + "_" + arch
-			selectKey := bazel.PlatformTargetMap[target]
-			archSelects[selectKey] = reflect.ValueOf(list.GetOsArchValueForTarget(os, arch))
+	var ret []selects
+	for _, axis := range list.SortedConfigurationAxes() {
+		configToLists := list.ConfigurableValues[axis]
+		archSelects := map[string]reflect.Value{}
+		for config, labels := range configToLists {
+			selectKey := axis.SelectKey(config)
+			archSelects[selectKey] = reflect.ValueOf(labels)
 		}
-		osArchSelects = append(osArchSelects, archSelects)
-	}
-	if len(osSelects) > 0 {
-		selectValues = append(selectValues, osSelects)
-	}
-	if len(osArchSelects) > 0 {
-		selectValues = append(selectValues, osArchSelects...)
-	}
-
-	for _, pv := range list.SortedProductVariables() {
-		s := make(selects)
-		if len(pv.Values) > 0 {
-			s[pv.SelectKey()] = reflect.ValueOf(pv.Values)
-			s[bazel.ConditionsDefaultSelectKey] = reflect.ValueOf([]string{})
-			selectValues = append(selectValues, s)
+		if len(archSelects) > 0 {
+			ret = append(ret, archSelects)
 		}
 	}
 
-	return value, selectValues
+	return value, ret
 }
 
 func getLabelValue(label bazel.LabelAttribute) (reflect.Value, []selects) {
@@ -65,105 +39,37 @@
 		return value, []selects{}
 	}
 
-	// Keep track of which arches and oses have been used in case we need to raise a warning
-	usedArches := make(map[string]bool)
-	usedOses := make(map[string]bool)
-
-	archSelects := map[string]reflect.Value{}
-	for arch, selectKey := range bazel.PlatformArchMap {
-		archSelects[selectKey] = reflect.ValueOf(label.GetValueForArch(arch))
-		if archSelects[selectKey].IsValid() && !isZero(archSelects[selectKey]) {
-			usedArches[arch] = true
+	ret := selects{}
+	for _, axis := range label.SortedConfigurationAxes() {
+		configToLabels := label.ConfigurableValues[axis]
+		for config, labels := range configToLabels {
+			selectKey := axis.SelectKey(config)
+			ret[selectKey] = reflect.ValueOf(labels)
 		}
 	}
 
-	osSelects := map[string]reflect.Value{}
-	for _, os := range android.SortedStringKeys(bazel.PlatformOsMap) {
-		selectKey := bazel.PlatformOsMap[os]
-		osSelects[selectKey] = reflect.ValueOf(label.GetOsValueForTarget(os))
-		if osSelects[selectKey].IsValid() && !isZero(osSelects[selectKey]) {
-			usedOses[os] = true
-		}
-	}
-
-	osArchSelects := make([]selects, 0)
-	for _, os := range android.SortedStringKeys(bazel.PlatformOsMap) {
-		archSelects := make(map[string]reflect.Value)
-		// TODO(b/187530594): Should we also check arch=CONDITIONS_DEFAULT? (not in AllArches)
-		for _, arch := range bazel.AllArches {
-			target := os + "_" + arch
-			selectKey := bazel.PlatformTargetMap[target]
-			archSelects[selectKey] = reflect.ValueOf(label.GetOsArchValueForTarget(os, arch))
-			if archSelects[selectKey].IsValid() && !isZero(archSelects[selectKey]) {
-				if _, ok := usedArches[arch]; ok {
-					fmt.Printf("WARNING: Same arch used twice in LabelAttribute select: arch '%s'\n", arch)
-				}
-				if _, ok := usedOses[os]; ok {
-					fmt.Printf("WARNING: Same os used twice in LabelAttribute select: os '%s'\n", os)
-				}
-			}
-		}
-		osArchSelects = append(osArchSelects, archSelects)
-	}
-
-	// Because we have to return a single Label, we can only use one select statement
-	combinedSelects := map[string]reflect.Value{}
-	for k, v := range archSelects {
-		combinedSelects[k] = v
-	}
-	for k, v := range osSelects {
-		combinedSelects[k] = v
-	}
-	for _, osArchSelect := range osArchSelects {
-		for k, v := range osArchSelect {
-			combinedSelects[k] = v
-		}
-	}
-
-	return value, []selects{combinedSelects}
+	return value, []selects{ret}
 }
 
 func getLabelListValues(list bazel.LabelListAttribute) (reflect.Value, []selects) {
 	value := reflect.ValueOf(list.Value.Includes)
-	if !list.HasConfigurableValues() {
-		return value, []selects{}
-	}
 	var ret []selects
-
-	archSelects := map[string]reflect.Value{}
-	for arch, selectKey := range bazel.PlatformArchMap {
-		if use, value := labelListSelectValue(selectKey, list.GetValueForArch(arch)); use {
-			archSelects[selectKey] = value
+	for _, axis := range list.SortedConfigurationAxes() {
+		configToLabels := list.ConfigurableValues[axis]
+		if !configToLabels.HasConfigurableValues() {
+			continue
 		}
-	}
-	if len(archSelects) > 0 {
-		ret = append(ret, archSelects)
-	}
-
-	osSelects := map[string]reflect.Value{}
-	osArchSelects := []selects{}
-	for _, os := range android.SortedStringKeys(bazel.PlatformOsMap) {
-		selectKey := bazel.PlatformOsMap[os]
-		if use, value := labelListSelectValue(selectKey, list.GetOsValueForTarget(os)); use {
-			osSelects[selectKey] = value
-		}
-		selects := make(map[string]reflect.Value)
-		// TODO(b/187530594): Should we also check arch=CONDITIOSN_DEFAULT? (not in AllArches)
-		for _, arch := range bazel.AllArches {
-			target := os + "_" + arch
-			selectKey := bazel.PlatformTargetMap[target]
-			if use, value := labelListSelectValue(selectKey, list.GetOsArchValueForTarget(os, arch)); use {
-				selects[selectKey] = value
+		archSelects := map[string]reflect.Value{}
+		for config, labels := range configToLabels {
+			selectKey := axis.SelectKey(config)
+			if use, value := labelListSelectValue(selectKey, labels); use {
+				archSelects[selectKey] = value
 			}
 		}
-		if len(selects) > 0 {
-			osArchSelects = append(osArchSelects, selects)
+		if len(archSelects) > 0 {
+			ret = append(ret, archSelects)
 		}
 	}
-	if len(osSelects) > 0 {
-		ret = append(ret, osSelects)
-	}
-	ret = append(ret, osArchSelects...)
 
 	return value, ret
 }
diff --git a/bp2build/testing.go b/bp2build/testing.go
index e575bc6..861f7d2 100644
--- a/bp2build/testing.go
+++ b/bp2build/testing.go
@@ -157,9 +157,11 @@
 
 		paths := bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrc(ctx, m.props.Arch_paths))
 
-		for arch, props := range m.GetArchProperties(ctx, &customProps{}) {
-			if archProps, ok := props.(*customProps); ok && archProps.Arch_paths != nil {
-				paths.SetValueForArch(arch.Name, android.BazelLabelForModuleSrc(ctx, archProps.Arch_paths))
+		for axis, configToProps := range m.GetArchVariantProperties(ctx, &customProps{}) {
+			for config, props := range configToProps {
+				if archProps, ok := props.(*customProps); ok && archProps.Arch_paths != nil {
+					paths.SetSelectValue(axis, config, android.BazelLabelForModuleSrc(ctx, archProps.Arch_paths))
+				}
 			}
 		}
 
diff --git a/cc/Android.bp b/cc/Android.bp
index 1fc8d9f..46740dc 100644
--- a/cc/Android.bp
+++ b/cc/Android.bp
@@ -65,9 +65,10 @@
         "test.go",
         "toolchain_library.go",
 
-        "ndk_prebuilt.go",
+        "ndk_abi.go",
         "ndk_headers.go",
         "ndk_library.go",
+        "ndk_prebuilt.go",
         "ndk_sysroot.go",
 
         "llndk_library.go",
diff --git a/cc/bp2build.go b/cc/bp2build.go
index a156d54..752d43b 100644
--- a/cc/bp2build.go
+++ b/cc/bp2build.go
@@ -53,58 +53,29 @@
 
 	var allDeps []string
 
-	for _, osProps := range module.GetTargetProperties(ctx, &BaseCompilerProperties{}) {
-		// os base compiler props
-		if baseCompilerProps, ok := osProps.Properties.(*BaseCompilerProperties); ok {
-			allDeps = append(allDeps, baseCompilerProps.Generated_headers...)
-			allDeps = append(allDeps, baseCompilerProps.Generated_sources...)
-		}
-		// os + arch base compiler props
-		for _, archProps := range osProps.ArchProperties {
-			if baseCompilerProps, ok := archProps.(*BaseCompilerProperties); ok {
+	for _, configToProps := range module.GetArchVariantProperties(ctx, &BaseCompilerProperties{}) {
+		for _, props := range configToProps {
+			if baseCompilerProps, ok := props.(*BaseCompilerProperties); ok {
 				allDeps = append(allDeps, baseCompilerProps.Generated_headers...)
 				allDeps = append(allDeps, baseCompilerProps.Generated_sources...)
 			}
 		}
 	}
 
-	for _, props := range module.GetArchProperties(ctx, &BaseCompilerProperties{}) {
-		// arch specific compiler props
-		if baseCompilerProps, ok := props.(*BaseCompilerProperties); ok {
-			allDeps = append(allDeps, baseCompilerProps.Generated_headers...)
-			allDeps = append(allDeps, baseCompilerProps.Generated_sources...)
-		}
-	}
-
-	for _, osProps := range module.GetTargetProperties(ctx, &BaseLinkerProperties{}) {
-		// os specific linker props
-		if baseLinkerProps, ok := osProps.Properties.(*BaseLinkerProperties); ok {
-			allDeps = append(allDeps, baseLinkerProps.Header_libs...)
-			allDeps = append(allDeps, baseLinkerProps.Export_header_lib_headers...)
-			allDeps = append(allDeps, baseLinkerProps.Static_libs...)
-			allDeps = append(allDeps, baseLinkerProps.Whole_static_libs...)
-		}
-		// os + arch base compiler props
-		for _, archProps := range osProps.ArchProperties {
-			if baseLinkerProps, ok := archProps.(*BaseLinkerProperties); ok {
+	for _, configToProps := range module.GetArchVariantProperties(ctx, &BaseLinkerProperties{}) {
+		for _, props := range configToProps {
+			if baseLinkerProps, ok := props.(*BaseLinkerProperties); ok {
 				allDeps = append(allDeps, baseLinkerProps.Header_libs...)
 				allDeps = append(allDeps, baseLinkerProps.Export_header_lib_headers...)
 				allDeps = append(allDeps, baseLinkerProps.Static_libs...)
+				allDeps = append(allDeps, baseLinkerProps.Exclude_static_libs...)
 				allDeps = append(allDeps, baseLinkerProps.Whole_static_libs...)
+				allDeps = append(allDeps, baseLinkerProps.Shared_libs...)
+				allDeps = append(allDeps, baseLinkerProps.Exclude_shared_libs...)
 			}
 		}
 	}
 
-	for _, props := range module.GetArchProperties(ctx, &BaseLinkerProperties{}) {
-		// arch specific linker props
-		if baseLinkerProps, ok := props.(*BaseLinkerProperties); ok {
-			allDeps = append(allDeps, baseLinkerProps.Header_libs...)
-			allDeps = append(allDeps, baseLinkerProps.Export_header_lib_headers...)
-			allDeps = append(allDeps, baseLinkerProps.Static_libs...)
-			allDeps = append(allDeps, baseLinkerProps.Whole_static_libs...)
-		}
-	}
-
 	// Deps in the static: { .. } and shared: { .. } props of a cc_library.
 	if lib, ok := module.compiler.(*libraryDecorator); ok {
 		appendDeps := func(deps []string, p StaticOrSharedProperties) []string {
@@ -124,37 +95,43 @@
 
 		// Deps in the target/arch nested static: { .. } and shared: { .. } props of a cc_library.
 		// target: { <target>: shared: { ... } }
-		for _, targetProps := range module.GetTargetProperties(ctx, &SharedProperties{}) {
-			if p, ok := targetProps.Properties.(*SharedProperties); ok {
-				allDeps = appendDeps(allDeps, p.Shared)
-			}
-			for _, archProperties := range targetProps.ArchProperties {
-				if p, ok := archProperties.(*SharedProperties); ok {
+		for _, configToProps := range module.GetArchVariantProperties(ctx, &SharedProperties{}) {
+			for _, props := range configToProps {
+				if p, ok := props.(*SharedProperties); ok {
 					allDeps = appendDeps(allDeps, p.Shared)
 				}
 			}
 		}
-		// target: { <target>: static: { ... } }
-		for _, targetProps := range module.GetTargetProperties(ctx, &StaticProperties{}) {
-			if p, ok := targetProps.Properties.(*StaticProperties); ok {
-				allDeps = appendDeps(allDeps, p.Static)
-			}
-			for _, archProperties := range targetProps.ArchProperties {
-				if p, ok := archProperties.(*StaticProperties); ok {
+
+		for _, configToProps := range module.GetArchVariantProperties(ctx, &StaticProperties{}) {
+			for _, props := range configToProps {
+				if p, ok := props.(*StaticProperties); ok {
 					allDeps = appendDeps(allDeps, p.Static)
 				}
 			}
 		}
-		// arch: { <arch>: shared: { ... } }
-		for _, properties := range module.GetArchProperties(ctx, &SharedProperties{}) {
-			if p, ok := properties.(*SharedProperties); ok {
-				allDeps = appendDeps(allDeps, p.Shared)
-			}
+	}
+
+	// product variables only support a limited set of fields, this is the full list of field names
+	// related to cc module dependency management that are supported.
+	productVariableDepFields := [4]string{
+		"Shared_libs",
+		"Static_libs",
+		"Exclude_static_libs",
+		"Whole_static_libs",
+	}
+
+	productVariableProps := android.ProductVariableProperties(ctx)
+	for _, name := range productVariableDepFields {
+		props, exists := productVariableProps[name]
+		if !exists {
+			continue
 		}
-		// arch: { <arch>: static: { ... } }
-		for _, properties := range module.GetArchProperties(ctx, &StaticProperties{}) {
-			if p, ok := properties.(*StaticProperties); ok {
-				allDeps = appendDeps(allDeps, p.Static)
+		for _, prop := range props {
+			if p, ok := prop.Property.([]string); !ok {
+				ctx.ModuleErrorf("Could not convert product variable %s property", name)
+			} else {
+				allDeps = append(allDeps, p...)
 			}
 		}
 	}
@@ -275,65 +252,33 @@
 
 	attrs := staticOrSharedAttributes{
 		copts:            bazel.StringListAttribute{Value: props.Cflags},
-		srcs:             bazel.LabelListAttribute{Value: android.BazelLabelForModuleSrc(ctx, props.Srcs)},
-		staticDeps:       bazel.LabelListAttribute{Value: android.BazelLabelForModuleDeps(ctx, props.Static_libs)},
-		dynamicDeps:      bazel.LabelListAttribute{Value: android.BazelLabelForModuleDeps(ctx, props.Shared_libs)},
-		wholeArchiveDeps: bazel.LabelListAttribute{Value: android.BazelLabelForModuleDeps(ctx, props.Whole_static_libs)},
+		srcs:             bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrc(ctx, props.Srcs)),
+		staticDeps:       bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, props.Static_libs)),
+		dynamicDeps:      bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, props.Shared_libs)),
+		wholeArchiveDeps: bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, props.Whole_static_libs)),
 	}
 
-	setArchAttrs := func(arch string, props StaticOrSharedProperties) {
-		attrs.copts.SetValueForArch(arch, props.Cflags)
-		attrs.srcs.SetValueForArch(arch, android.BazelLabelForModuleSrc(ctx, props.Srcs))
-		attrs.staticDeps.SetValueForArch(arch, android.BazelLabelForModuleDeps(ctx, props.Static_libs))
-		attrs.dynamicDeps.SetValueForArch(arch, android.BazelLabelForModuleDeps(ctx, props.Shared_libs))
-		attrs.wholeArchiveDeps.SetValueForArch(arch, android.BazelLabelForModuleDeps(ctx, props.Whole_static_libs))
-	}
-
-	setTargetAttrs := func(target string, props StaticOrSharedProperties) {
-		attrs.copts.SetOsValueForTarget(target, props.Cflags)
-		attrs.srcs.SetOsValueForTarget(target, android.BazelLabelForModuleSrc(ctx, props.Srcs))
-		attrs.staticDeps.SetOsValueForTarget(target, android.BazelLabelForModuleDeps(ctx, props.Static_libs))
-		attrs.dynamicDeps.SetOsValueForTarget(target, android.BazelLabelForModuleDeps(ctx, props.Shared_libs))
-		attrs.wholeArchiveDeps.SetOsValueForTarget(target, android.BazelLabelForModuleDeps(ctx, props.Whole_static_libs))
-	}
-
-	setTargetArchAttrs := func(target, arch string, props StaticOrSharedProperties) {
-		attrs.copts.SetOsArchValueForTarget(target, arch, props.Cflags)
-		attrs.srcs.SetOsArchValueForTarget(target, arch, android.BazelLabelForModuleSrc(ctx, props.Srcs))
-		attrs.staticDeps.SetOsArchValueForTarget(target, arch, android.BazelLabelForModuleDeps(ctx, props.Static_libs))
-		attrs.dynamicDeps.SetOsArchValueForTarget(target, arch, android.BazelLabelForModuleDeps(ctx, props.Shared_libs))
-		attrs.wholeArchiveDeps.SetOsArchValueForTarget(target, arch, android.BazelLabelForModuleDeps(ctx, props.Whole_static_libs))
+	setAttrs := func(axis bazel.ConfigurationAxis, config string, props StaticOrSharedProperties) {
+		attrs.copts.SetSelectValue(axis, config, props.Cflags)
+		attrs.srcs.SetSelectValue(axis, config, android.BazelLabelForModuleSrc(ctx, props.Srcs))
+		attrs.staticDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, props.Static_libs))
+		attrs.dynamicDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, props.Shared_libs))
+		attrs.wholeArchiveDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, props.Whole_static_libs))
 	}
 
 	if isStatic {
-		for arch, properties := range module.GetArchProperties(ctx, &StaticProperties{}) {
-			if staticOrSharedProps, ok := properties.(*StaticProperties); ok {
-				setArchAttrs(arch.Name, staticOrSharedProps.Static)
-			}
-		}
-		for target, p := range module.GetTargetProperties(ctx, &StaticProperties{}) {
-			if staticOrSharedProps, ok := p.Properties.(*StaticProperties); ok {
-				setTargetAttrs(target.Name, staticOrSharedProps.Static)
-			}
-			for arch, archProperties := range p.ArchProperties {
-				if staticOrSharedProps, ok := archProperties.(*StaticProperties); ok {
-					setTargetArchAttrs(target.Name, arch.Name, staticOrSharedProps.Static)
+		for axis, configToProps := range module.GetArchVariantProperties(ctx, &StaticProperties{}) {
+			for config, props := range configToProps {
+				if staticOrSharedProps, ok := props.(*StaticProperties); ok {
+					setAttrs(axis, config, staticOrSharedProps.Static)
 				}
 			}
 		}
 	} else {
-		for arch, p := range module.GetArchProperties(ctx, &SharedProperties{}) {
-			if staticOrSharedProps, ok := p.(*SharedProperties); ok {
-				setArchAttrs(arch.Name, staticOrSharedProps.Shared)
-			}
-		}
-		for target, p := range module.GetTargetProperties(ctx, &SharedProperties{}) {
-			if staticOrSharedProps, ok := p.Properties.(*SharedProperties); ok {
-				setTargetAttrs(target.Name, staticOrSharedProps.Shared)
-			}
-			for arch, archProperties := range p.ArchProperties {
-				if staticOrSharedProps, ok := archProperties.(*SharedProperties); ok {
-					setTargetArchAttrs(target.Name, arch.Name, staticOrSharedProps.Shared)
+		for axis, configToProps := range module.GetArchVariantProperties(ctx, &SharedProperties{}) {
+			for config, props := range configToProps {
+				if staticOrSharedProps, ok := props.(*SharedProperties); ok {
+					setAttrs(axis, config, staticOrSharedProps.Shared)
 				}
 			}
 		}
@@ -363,45 +308,23 @@
 	}
 
 	if len(prebuiltLinker.properties.Srcs) == 1 {
-		srcLabelAttribute.Value = android.BazelLabelForModuleSrcSingle(ctx, prebuiltLinker.properties.Srcs[0])
-		for arch, props := range module.GetArchProperties(ctx, &prebuiltLinkerProperties{}) {
+		srcLabelAttribute.SetValue(android.BazelLabelForModuleSrcSingle(ctx, prebuiltLinker.properties.Srcs[0]))
+	}
+	for axis, configToProps := range module.GetArchVariantProperties(ctx, &prebuiltLinkerProperties{}) {
+		for config, props := range configToProps {
 			if prebuiltLinkerProperties, ok := props.(*prebuiltLinkerProperties); ok {
 				if len(prebuiltLinkerProperties.Srcs) > 1 {
-					ctx.ModuleErrorf("Bp2BuildParsePrebuiltLibraryProps: Expected at most once source file for arch %s\n", arch.Name)
+					ctx.ModuleErrorf("Bp2BuildParsePrebuiltLibraryProps: Expected at most once source file for %s %s\n", axis, config)
+					continue
+				} else if len(prebuiltLinkerProperties.Srcs) == 0 {
+					continue
 				}
-				if len(prebuiltLinkerProperties.Srcs) == 1 {
-					srcLabelAttribute.SetValueForArch(arch.Name, android.BazelLabelForModuleSrcSingle(ctx, prebuiltLinkerProperties.Srcs[0]))
-				}
+				src := android.BazelLabelForModuleSrcSingle(ctx, prebuiltLinkerProperties.Srcs[0])
+				srcLabelAttribute.SetSelectValue(axis, config, src)
 			}
 		}
 	}
 
-	for os, targetProperties := range module.GetTargetProperties(ctx, &prebuiltLinkerProperties{}) {
-		if prebuiltLinkerProperties, ok := targetProperties.Properties.(*prebuiltLinkerProperties); ok {
-			if len(prebuiltLinkerProperties.Srcs) > 1 {
-				ctx.ModuleErrorf("Bp2BuildParsePrebuiltLibraryProps: Expected at most once source file for os %s\n", os.Name)
-
-			}
-
-			if len(prebuiltLinkerProperties.Srcs) == 1 {
-				srcLabelAttribute.SetOsValueForTarget(os.Name, android.BazelLabelForModuleSrcSingle(ctx, prebuiltLinkerProperties.Srcs[0]))
-			}
-		}
-		for arch, archProperties := range targetProperties.ArchProperties {
-			if prebuiltLinkerProperties, ok := archProperties.(*prebuiltLinkerProperties); ok {
-				if len(prebuiltLinkerProperties.Srcs) > 1 {
-					ctx.ModuleErrorf("Bp2BuildParsePrebuiltLibraryProps: Expected at most once source file for os_arch %s_%s\n", os.Name, arch.Name)
-
-				}
-
-				if len(prebuiltLinkerProperties.Srcs) == 1 {
-					srcLabelAttribute.SetOsArchValueForTarget(os.Name, arch.Name, android.BazelLabelForModuleSrcSingle(ctx, prebuiltLinkerProperties.Srcs[0]))
-				}
-			}
-
-		}
-	}
-
 	return prebuiltAttributes{
 		Src: srcLabelAttribute,
 	}
@@ -473,44 +396,26 @@
 		return copts
 	}
 
-	// baseSrcs contain the list of src files that are used for every configuration.
-	var baseSrcs []string
-	// baseExcludeSrcs contain the list of src files that are excluded for every configuration.
-	var baseExcludeSrcs []string
-	// baseSrcsLabelList is a clone of the base srcs LabelList, used for computing the
-	// arch or os specific srcs later.
-	var baseSrcsLabelList bazel.LabelList
-
-	// Parse srcs from an arch or OS's props value, taking the base srcs and
-	// exclude srcs into account.
+	// Parse srcs from an arch or OS's props value.
 	parseSrcs := func(baseCompilerProps *BaseCompilerProperties) bazel.LabelList {
-		// Combine the base srcs and arch-specific srcs
-		allSrcs := append(baseSrcs, baseCompilerProps.Srcs...)
 		// Add srcs-like dependencies such as generated files.
 		// First create a LabelList containing these dependencies, then merge the values with srcs.
 		generatedHdrsAndSrcs := baseCompilerProps.Generated_headers
 		generatedHdrsAndSrcs = append(generatedHdrsAndSrcs, baseCompilerProps.Generated_sources...)
-
 		generatedHdrsAndSrcsLabelList := android.BazelLabelForModuleDeps(ctx, generatedHdrsAndSrcs)
 
-		// Combine the base exclude_srcs and configuration-specific exclude_srcs
-		allExcludeSrcs := append(baseExcludeSrcs, baseCompilerProps.Exclude_srcs...)
-		allSrcsLabelList := android.BazelLabelForModuleSrcExcludes(ctx, allSrcs, allExcludeSrcs)
+		allSrcsLabelList := android.BazelLabelForModuleSrcExcludes(ctx, baseCompilerProps.Srcs, baseCompilerProps.Exclude_srcs)
 		return bazel.AppendBazelLabelLists(allSrcsLabelList, generatedHdrsAndSrcsLabelList)
 	}
 
 	for _, props := range module.compiler.compilerProps() {
 		if baseCompilerProps, ok := props.(*BaseCompilerProperties); ok {
-			srcs.Value = parseSrcs(baseCompilerProps)
+			srcs.SetValue(parseSrcs(baseCompilerProps))
 			copts.Value = parseCopts(baseCompilerProps)
 			asFlags.Value = parseCommandLineFlags(baseCompilerProps.Asflags)
 			conlyFlags.Value = parseCommandLineFlags(baseCompilerProps.Conlyflags)
 			cppFlags.Value = parseCommandLineFlags(baseCompilerProps.Cppflags)
 
-			// Used for arch-specific srcs later.
-			baseSrcs = baseCompilerProps.Srcs
-			baseSrcsLabelList = parseSrcs(baseCompilerProps)
-			baseExcludeSrcs = baseCompilerProps.Exclude_srcs
 			break
 		}
 	}
@@ -524,67 +429,28 @@
 		copts.Value = append(copts.Value, includeFlags(".")...)
 	}
 
-	for arch, props := range module.GetArchProperties(ctx, &BaseCompilerProperties{}) {
-		if baseCompilerProps, ok := props.(*BaseCompilerProperties); ok {
-			// If there's arch specific srcs or exclude_srcs, generate a select entry for it.
-			// TODO(b/186153868): do this for OS specific srcs and exclude_srcs too.
-			if len(baseCompilerProps.Srcs) > 0 || len(baseCompilerProps.Exclude_srcs) > 0 {
-				srcsList := parseSrcs(baseCompilerProps)
-				srcs.SetValueForArch(arch.Name, srcsList)
-				// The base srcs value should not contain any arch-specific excludes.
-				srcs.Value = bazel.SubtractBazelLabelList(srcs.Value, bazel.LabelList{Includes: srcsList.Excludes})
-			}
+	archVariantCompilerProps := module.GetArchVariantProperties(ctx, &BaseCompilerProperties{})
 
-			copts.SetValueForArch(arch.Name, parseCopts(baseCompilerProps))
-			asFlags.SetValueForArch(arch.Name, parseCommandLineFlags(baseCompilerProps.Asflags))
-			conlyFlags.SetValueForArch(arch.Name, parseCommandLineFlags(baseCompilerProps.Conlyflags))
-			cppFlags.SetValueForArch(arch.Name, parseCommandLineFlags(baseCompilerProps.Cppflags))
-		}
-	}
-
-	// After going through all archs, delete the duplicate files in the arch
-	// values that are already in the base srcs.Value.
-	for arch, props := range module.GetArchProperties(ctx, &BaseCompilerProperties{}) {
-		if _, ok := props.(*BaseCompilerProperties); ok {
-			srcs.SetValueForArch(arch.Name, bazel.SubtractBazelLabelList(srcs.GetValueForArch(arch.Name), srcs.Value))
-		}
-	}
-
-	// Now that the srcs.Value list is finalized, compare it with the original
-	// list, and put the difference into the default condition for the arch
-	// select.
-	defaultsSrcs := bazel.SubtractBazelLabelList(baseSrcsLabelList, srcs.Value)
-	// TODO(b/186153868): handle the case with multiple variant types, e.g. when arch and os are both used.
-	srcs.SetValueForArch(bazel.CONDITIONS_DEFAULT, defaultsSrcs)
-
-	// Handle target specific properties.
-	for os, osProps := range module.GetTargetProperties(ctx, &BaseCompilerProperties{}) {
-		if baseCompilerProps, ok := osProps.Properties.(*BaseCompilerProperties); ok {
-			srcsList := parseSrcs(baseCompilerProps)
-			// TODO(b/186153868): add support for os-specific srcs and exclude_srcs
-			if len(baseCompilerProps.Srcs) > 0 || len(baseCompilerProps.Exclude_srcs) > 0 {
-				srcs.SetOsValueForTarget(os.Name, bazel.SubtractBazelLabelList(srcsList, baseSrcsLabelList))
-			}
-			copts.SetOsValueForTarget(os.Name, parseCopts(baseCompilerProps))
-			asFlags.SetOsValueForTarget(os.Name, parseCommandLineFlags(baseCompilerProps.Asflags))
-			conlyFlags.SetOsValueForTarget(os.Name, parseCommandLineFlags(baseCompilerProps.Conlyflags))
-			cppFlags.SetOsValueForTarget(os.Name, parseCommandLineFlags(baseCompilerProps.Cppflags))
-		}
-		for arch, archProps := range osProps.ArchProperties {
-			if baseCompilerProps, ok := archProps.(*BaseCompilerProperties); ok {
-				srcsList := parseSrcs(baseCompilerProps)
-				// TODO(b/186153868): add support for os-specific srcs and exclude_srcs
+	for axis, configToProps := range archVariantCompilerProps {
+		for config, props := range configToProps {
+			if baseCompilerProps, ok := props.(*BaseCompilerProperties); ok {
+				// If there's arch specific srcs or exclude_srcs, generate a select entry for it.
+				// TODO(b/186153868): do this for OS specific srcs and exclude_srcs too.
 				if len(baseCompilerProps.Srcs) > 0 || len(baseCompilerProps.Exclude_srcs) > 0 {
-					srcs.SetOsArchValueForTarget(os.Name, arch.Name, bazel.SubtractBazelLabelList(srcsList, baseSrcsLabelList))
+					srcsList := parseSrcs(baseCompilerProps)
+					srcs.SetSelectValue(axis, config, srcsList)
 				}
-				copts.SetOsArchValueForTarget(os.Name, arch.Name, parseCopts(baseCompilerProps))
-				asFlags.SetOsArchValueForTarget(os.Name, arch.Name, parseCommandLineFlags(baseCompilerProps.Asflags))
-				conlyFlags.SetOsArchValueForTarget(os.Name, arch.Name, parseCommandLineFlags(baseCompilerProps.Conlyflags))
-				cppFlags.SetOsArchValueForTarget(os.Name, arch.Name, parseCommandLineFlags(baseCompilerProps.Cppflags))
+
+				copts.SetSelectValue(axis, config, parseCopts(baseCompilerProps))
+				asFlags.SetSelectValue(axis, config, parseCommandLineFlags(baseCompilerProps.Asflags))
+				conlyFlags.SetSelectValue(axis, config, parseCommandLineFlags(baseCompilerProps.Conlyflags))
+				cppFlags.SetSelectValue(axis, config, parseCommandLineFlags(baseCompilerProps.Cppflags))
 			}
 		}
 	}
 
+	srcs.ResolveExcludes()
+
 	productVarPropNameToAttribute := map[string]*bazel.StringListAttribute{
 		"Cflags":   &copts,
 		"Asflags":  &asFlags,
@@ -599,10 +465,7 @@
 					ctx.ModuleErrorf("Could not convert product variable %s property", proptools.PropertyNameForField(propName))
 				}
 				newFlags, _ := bazel.TryVariableSubstitutions(flags, prop.ProductConfigVariable)
-				attr.ProductValues = append(attr.ProductValues, bazel.ProductVariableValues{
-					ProductVariable: prop.ProductConfigVariable,
-					Values:          newFlags,
-				})
+				attr.SetSelectValue(bazel.ProductVariableConfigurationAxis(prop.FullConfig), prop.FullConfig, newFlags)
 			}
 		}
 	}
@@ -642,102 +505,121 @@
 // bp2BuildParseLinkerProps parses the linker properties of a module, including
 // configurable attribute values.
 func bp2BuildParseLinkerProps(ctx android.TopDownMutatorContext, module *Module) linkerAttributes {
-	var deps bazel.LabelListAttribute
+	var headerDeps bazel.LabelListAttribute
+	var staticDeps bazel.LabelListAttribute
 	var exportedDeps bazel.LabelListAttribute
 	var dynamicDeps bazel.LabelListAttribute
 	var wholeArchiveDeps bazel.LabelListAttribute
 	var linkopts bazel.StringListAttribute
 	var versionScript bazel.LabelAttribute
 
-	getLibs := func(baseLinkerProps *BaseLinkerProperties) []string {
-		libs := baseLinkerProps.Header_libs
-		libs = append(libs, baseLinkerProps.Static_libs...)
-		libs = android.SortedUniqueStrings(libs)
-		return libs
-	}
-
 	for _, linkerProps := range module.linker.linkerProps() {
 		if baseLinkerProps, ok := linkerProps.(*BaseLinkerProperties); ok {
-			libs := getLibs(baseLinkerProps)
-			exportedLibs := baseLinkerProps.Export_header_lib_headers
-			wholeArchiveLibs := baseLinkerProps.Whole_static_libs
-			deps = bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, libs))
+			// Excludes to parallel Soong:
+			// https://cs.android.com/android/platform/superproject/+/master:build/soong/cc/linker.go;l=247-249;drc=088b53577dde6e40085ffd737a1ae96ad82fc4b0
+			staticLibs := android.FirstUniqueStrings(baseLinkerProps.Static_libs)
+			staticDeps.Value = android.BazelLabelForModuleDepsExcludes(ctx, staticLibs, baseLinkerProps.Exclude_static_libs)
+			wholeArchiveLibs := android.FirstUniqueStrings(baseLinkerProps.Whole_static_libs)
+			wholeArchiveDeps = bazel.MakeLabelListAttribute(android.BazelLabelForModuleDepsExcludes(ctx, wholeArchiveLibs, baseLinkerProps.Exclude_static_libs))
+			sharedLibs := android.FirstUniqueStrings(baseLinkerProps.Shared_libs)
+			dynamicDeps = bazel.MakeLabelListAttribute(android.BazelLabelForModuleDepsExcludes(ctx, sharedLibs, baseLinkerProps.Exclude_shared_libs))
+
+			headerLibs := android.FirstUniqueStrings(baseLinkerProps.Header_libs)
+			headerDeps = bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, headerLibs))
+			// TODO(b/188796939): also handle export_static_lib_headers, export_shared_lib_headers,
+			// export_generated_headers
+			exportedLibs := android.FirstUniqueStrings(baseLinkerProps.Export_header_lib_headers)
 			exportedDeps = bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, exportedLibs))
+
 			linkopts.Value = getBp2BuildLinkerFlags(baseLinkerProps)
-			wholeArchiveDeps = bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, wholeArchiveLibs))
-
 			if baseLinkerProps.Version_script != nil {
-				versionScript.Value = android.BazelLabelForModuleSrcSingle(ctx, *baseLinkerProps.Version_script)
+				versionScript.SetValue(android.BazelLabelForModuleSrcSingle(ctx, *baseLinkerProps.Version_script))
 			}
 
-			sharedLibs := baseLinkerProps.Shared_libs
-			dynamicDeps = bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, sharedLibs))
-
 			break
 		}
 	}
 
-	for arch, props := range module.GetArchProperties(ctx, &BaseLinkerProperties{}) {
-		if baseLinkerProps, ok := props.(*BaseLinkerProperties); ok {
-			libs := getLibs(baseLinkerProps)
-			exportedLibs := baseLinkerProps.Export_header_lib_headers
-			wholeArchiveLibs := baseLinkerProps.Whole_static_libs
-			deps.SetValueForArch(arch.Name, android.BazelLabelForModuleDeps(ctx, libs))
-			exportedDeps.SetValueForArch(arch.Name, android.BazelLabelForModuleDeps(ctx, exportedLibs))
-			linkopts.SetValueForArch(arch.Name, getBp2BuildLinkerFlags(baseLinkerProps))
-			wholeArchiveDeps.SetValueForArch(arch.Name, android.BazelLabelForModuleDeps(ctx, wholeArchiveLibs))
+	for axis, configToProps := range module.GetArchVariantProperties(ctx, &BaseLinkerProperties{}) {
+		for config, props := range configToProps {
+			if baseLinkerProps, ok := props.(*BaseLinkerProperties); ok {
+				staticLibs := android.FirstUniqueStrings(baseLinkerProps.Static_libs)
+				staticDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDepsExcludes(ctx, staticLibs, baseLinkerProps.Exclude_static_libs))
+				wholeArchiveLibs := android.FirstUniqueStrings(baseLinkerProps.Whole_static_libs)
+				wholeArchiveDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDepsExcludes(ctx, wholeArchiveLibs, baseLinkerProps.Exclude_static_libs))
+				sharedLibs := android.FirstUniqueStrings(baseLinkerProps.Shared_libs)
+				dynamicDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDepsExcludes(ctx, sharedLibs, baseLinkerProps.Exclude_shared_libs))
 
-			if baseLinkerProps.Version_script != nil {
-				versionScript.SetValueForArch(arch.Name,
-					android.BazelLabelForModuleSrcSingle(ctx, *baseLinkerProps.Version_script))
-			}
+				headerLibs := android.FirstUniqueStrings(baseLinkerProps.Header_libs)
+				headerDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, headerLibs))
+				exportedLibs := android.FirstUniqueStrings(baseLinkerProps.Export_header_lib_headers)
+				exportedDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, exportedLibs))
 
-			sharedLibs := baseLinkerProps.Shared_libs
-			dynamicDeps.SetValueForArch(arch.Name, android.BazelLabelForModuleDeps(ctx, sharedLibs))
-		}
-	}
-
-	for os, targetProperties := range module.GetTargetProperties(ctx, &BaseLinkerProperties{}) {
-		if baseLinkerProps, ok := targetProperties.Properties.(*BaseLinkerProperties); ok {
-			libs := getLibs(baseLinkerProps)
-			exportedLibs := baseLinkerProps.Export_header_lib_headers
-			wholeArchiveLibs := baseLinkerProps.Whole_static_libs
-			wholeArchiveDeps.SetOsValueForTarget(os.Name, android.BazelLabelForModuleDeps(ctx, wholeArchiveLibs))
-			deps.SetOsValueForTarget(os.Name, android.BazelLabelForModuleDeps(ctx, libs))
-			exportedDeps.SetOsValueForTarget(os.Name, android.BazelLabelForModuleDeps(ctx, exportedLibs))
-
-			linkopts.SetOsValueForTarget(os.Name, getBp2BuildLinkerFlags(baseLinkerProps))
-
-			if baseLinkerProps.Version_script != nil {
-				versionScript.SetOsValueForTarget(os.Name, android.BazelLabelForModuleSrcSingle(ctx, *baseLinkerProps.Version_script))
-			}
-
-			sharedLibs := baseLinkerProps.Shared_libs
-			dynamicDeps.SetOsValueForTarget(os.Name, android.BazelLabelForModuleDeps(ctx, sharedLibs))
-		}
-		for arch, archProperties := range targetProperties.ArchProperties {
-			if baseLinkerProps, ok := archProperties.(*BaseLinkerProperties); ok {
-				libs := getLibs(baseLinkerProps)
-				exportedLibs := baseLinkerProps.Export_header_lib_headers
-				wholeArchiveLibs := baseLinkerProps.Whole_static_libs
-				wholeArchiveDeps.SetOsArchValueForTarget(os.Name, arch.Name, android.BazelLabelForModuleDeps(ctx, wholeArchiveLibs))
-				deps.SetOsArchValueForTarget(os.Name, arch.Name, android.BazelLabelForModuleDeps(ctx, libs))
-				exportedDeps.SetOsArchValueForTarget(os.Name, arch.Name, android.BazelLabelForModuleDeps(ctx, exportedLibs))
-
-				linkopts.SetOsArchValueForTarget(os.Name, arch.Name, getBp2BuildLinkerFlags(baseLinkerProps))
-
+				linkopts.SetSelectValue(axis, config, getBp2BuildLinkerFlags(baseLinkerProps))
 				if baseLinkerProps.Version_script != nil {
-					versionScript.SetOsArchValueForTarget(os.Name, arch.Name, android.BazelLabelForModuleSrcSingle(ctx, *baseLinkerProps.Version_script))
+					versionScript.SetSelectValue(axis, config, android.BazelLabelForModuleSrcSingle(ctx, *baseLinkerProps.Version_script))
 				}
-
-				sharedLibs := baseLinkerProps.Shared_libs
-				dynamicDeps.SetOsArchValueForTarget(os.Name, arch.Name, android.BazelLabelForModuleDeps(ctx, sharedLibs))
 			}
 		}
 	}
 
+	type productVarDep struct {
+		// the name of the corresponding excludes field, if one exists
+		excludesField string
+		// reference to the bazel attribute that should be set for the given product variable config
+		attribute *bazel.LabelListAttribute
+	}
+
+	productVarToDepFields := map[string]productVarDep{
+		// product variables do not support exclude_shared_libs
+		"Shared_libs":       productVarDep{attribute: &dynamicDeps},
+		"Static_libs":       productVarDep{"Exclude_static_libs", &staticDeps},
+		"Whole_static_libs": productVarDep{"Exclude_static_libs", &wholeArchiveDeps},
+	}
+
+	productVariableProps := android.ProductVariableProperties(ctx)
+	for name, dep := range productVarToDepFields {
+		props, exists := productVariableProps[name]
+		excludeProps, excludesExists := productVariableProps[dep.excludesField]
+		// if neither an include or excludes property exists, then skip it
+		if !exists && !excludesExists {
+			continue
+		}
+		// collect all the configurations that an include or exclude property exists for.
+		// we want to iterate all configurations rather than either the include or exclude because for a
+		// particular configuration we may have only and include or only an exclude to handle
+		configs := make(map[string]bool, len(props)+len(excludeProps))
+		for config := range props {
+			configs[config] = true
+		}
+		for config := range excludeProps {
+			configs[config] = true
+		}
+
+		for config := range configs {
+			prop, includesExists := props[config]
+			excludesProp, excludesExists := excludeProps[config]
+			var includes, excludes []string
+			var ok bool
+			// if there was no includes/excludes property, casting fails and that's expected
+			if includes, ok = prop.Property.([]string); includesExists && !ok {
+				ctx.ModuleErrorf("Could not convert product variable %s property", name)
+			}
+			if excludes, ok = excludesProp.Property.([]string); excludesExists && !ok {
+				ctx.ModuleErrorf("Could not convert product variable %s property", dep.excludesField)
+			}
+			dep.attribute.SetSelectValue(bazel.ProductVariableConfigurationAxis(config), config, android.BazelLabelForModuleDepsExcludes(ctx, android.FirstUniqueStrings(includes), excludes))
+		}
+	}
+
+	staticDeps.ResolveExcludes()
+	dynamicDeps.ResolveExcludes()
+	wholeArchiveDeps.ResolveExcludes()
+
+	headerDeps.Append(staticDeps)
+
 	return linkerAttributes{
-		deps:             deps,
+		deps:             headerDeps,
 		exportedDeps:     exportedDeps,
 		dynamicDeps:      dynamicDeps,
 		wholeArchiveDeps: wholeArchiveDeps,
@@ -800,27 +682,12 @@
 		return variantIncludeDirs
 	}
 
-	for arch, props := range module.GetArchProperties(ctx, &FlagExporterProperties{}) {
-		if flagExporterProperties, ok := props.(*FlagExporterProperties); ok {
-			archIncludeDirs := getVariantIncludeDirs(includeDirs, flagExporterProperties)
-			if len(archIncludeDirs) > 0 {
-				includeDirsAttribute.SetValueForArch(arch.Name, archIncludeDirs)
-			}
-		}
-	}
-
-	for os, targetProperties := range module.GetTargetProperties(ctx, &FlagExporterProperties{}) {
-		if flagExporterProperties, ok := targetProperties.Properties.(*FlagExporterProperties); ok {
-			targetIncludeDirs := getVariantIncludeDirs(includeDirs, flagExporterProperties)
-			if len(targetIncludeDirs) > 0 {
-				includeDirsAttribute.SetOsValueForTarget(os.Name, targetIncludeDirs)
-			}
-		}
-		for arch, archProperties := range targetProperties.ArchProperties {
-			if flagExporterProperties, ok := archProperties.(*FlagExporterProperties); ok {
-				targetIncludeDirs := getVariantIncludeDirs(includeDirs, flagExporterProperties)
-				if len(targetIncludeDirs) > 0 {
-					includeDirsAttribute.SetOsArchValueForTarget(os.Name, arch.Name, targetIncludeDirs)
+	for axis, configToProps := range module.GetArchVariantProperties(ctx, &FlagExporterProperties{}) {
+		for config, props := range configToProps {
+			if flagExporterProperties, ok := props.(*FlagExporterProperties); ok {
+				archVariantIncludeDirs := getVariantIncludeDirs(includeDirs, flagExporterProperties)
+				if len(archVariantIncludeDirs) > 0 {
+					includeDirsAttribute.SetSelectValue(axis, config, archVariantIncludeDirs)
 				}
 			}
 		}
diff --git a/cc/cc.go b/cc/cc.go
index 91c4417..7c4ba44 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -1267,8 +1267,8 @@
 }
 
 func (c *Module) IsSnapshotPrebuilt() bool {
-	if p, ok := c.linker.(snapshotInterface); ok {
-		return p.isSnapshotPrebuilt()
+	if p, ok := c.linker.(SnapshotInterface); ok {
+		return p.IsSnapshotPrebuilt()
 	}
 	return false
 }
@@ -1980,15 +1980,19 @@
 	}
 }
 
-func (c *Module) addSharedLibDependenciesWithVersions(ctx android.BottomUpMutatorContext,
-	variations []blueprint.Variation, depTag libraryDependencyTag, name, version string, far bool) {
+func AddSharedLibDependenciesWithVersions(ctx android.BottomUpMutatorContext, mod LinkableInterface,
+	variations []blueprint.Variation, depTag blueprint.DependencyTag, name, version string, far bool) {
 
 	variations = append([]blueprint.Variation(nil), variations...)
 
-	if version != "" && CanBeOrLinkAgainstVersionVariants(c) {
+	if version != "" && CanBeOrLinkAgainstVersionVariants(mod) {
 		// Version is explicitly specified. i.e. libFoo#30
 		variations = append(variations, blueprint.Variation{Mutator: "version", Variation: version})
-		depTag.explicitlyVersioned = true
+		if tag, ok := depTag.(libraryDependencyTag); ok {
+			tag.explicitlyVersioned = true
+		} else {
+			panic(fmt.Errorf("Unexpected dependency tag: %T", depTag))
+		}
 	}
 
 	if far {
@@ -1998,6 +2002,74 @@
 	}
 }
 
+func GetSnapshot(c LinkableInterface, snapshotInfo **SnapshotInfo, actx android.BottomUpMutatorContext) SnapshotInfo {
+	// Only modules with BOARD_VNDK_VERSION uses snapshot.  Others use the zero value of
+	// SnapshotInfo, which provides no mappings.
+	if *snapshotInfo == nil {
+		// Only retrieve the snapshot on demand in order to avoid circular dependencies
+		// between the modules in the snapshot and the snapshot itself.
+		var snapshotModule []blueprint.Module
+		if c.InVendor() && c.VndkVersion() == actx.DeviceConfig().VndkVersion() {
+			snapshotModule = actx.AddVariationDependencies(nil, nil, "vendor_snapshot")
+		} else if recoverySnapshotVersion := actx.DeviceConfig().RecoverySnapshotVersion(); recoverySnapshotVersion != "current" && recoverySnapshotVersion != "" && c.InRecovery() {
+			snapshotModule = actx.AddVariationDependencies(nil, nil, "recovery_snapshot")
+		}
+		if len(snapshotModule) > 0 {
+			snapshot := actx.OtherModuleProvider(snapshotModule[0], SnapshotInfoProvider).(SnapshotInfo)
+			*snapshotInfo = &snapshot
+			// republish the snapshot for use in later mutators on this module
+			actx.SetProvider(SnapshotInfoProvider, snapshot)
+		} else {
+			*snapshotInfo = &SnapshotInfo{}
+		}
+	}
+
+	return **snapshotInfo
+}
+
+func RewriteSnapshotLib(lib string, snapshotMap map[string]string) string {
+	if snapshot, ok := snapshotMap[lib]; ok {
+		return snapshot
+	}
+
+	return lib
+}
+
+// RewriteLibs takes a list of names of shared libraries and scans it for three types
+// of names:
+//
+// 1. Name of an NDK library that refers to a prebuilt module.
+//    For each of these, it adds the name of the prebuilt module (which will be in
+//    prebuilts/ndk) to the list of nonvariant libs.
+// 2. Name of an NDK library that refers to an ndk_library module.
+//    For each of these, it adds the name of the ndk_library module to the list of
+//    variant libs.
+// 3. Anything else (so anything that isn't an NDK library).
+//    It adds these to the nonvariantLibs list.
+//
+// The caller can then know to add the variantLibs dependencies differently from the
+// nonvariantLibs
+func RewriteLibs(c LinkableInterface, snapshotInfo **SnapshotInfo, actx android.BottomUpMutatorContext, config android.Config, list []string) (nonvariantLibs []string, variantLibs []string) {
+	variantLibs = []string{}
+
+	nonvariantLibs = []string{}
+	for _, entry := range list {
+		// strip #version suffix out
+		name, _ := StubsLibNameAndVersion(entry)
+		if c.InRecovery() {
+			nonvariantLibs = append(nonvariantLibs, RewriteSnapshotLib(entry, GetSnapshot(c, snapshotInfo, actx).SharedLibs))
+		} else if c.UseSdk() && inList(name, *getNDKKnownLibs(config)) {
+			variantLibs = append(variantLibs, name+ndkLibrarySuffix)
+		} else if c.UseVndk() {
+			nonvariantLibs = append(nonvariantLibs, RewriteSnapshotLib(entry, GetSnapshot(c, snapshotInfo, actx).SharedLibs))
+		} else {
+			// put name#version back
+			nonvariantLibs = append(nonvariantLibs, entry)
+		}
+	}
+	return nonvariantLibs, variantLibs
+}
+
 func (c *Module) DepsMutator(actx android.BottomUpMutatorContext) {
 	if !c.Enabled() {
 		return
@@ -2016,83 +2088,16 @@
 	c.Properties.AndroidMkSystemSharedLibs = deps.SystemSharedLibs
 
 	var snapshotInfo *SnapshotInfo
-	getSnapshot := func() SnapshotInfo {
-		// Only modules with BOARD_VNDK_VERSION uses snapshot.  Others use the zero value of
-		// SnapshotInfo, which provides no mappings.
-		if snapshotInfo == nil {
-			// Only retrieve the snapshot on demand in order to avoid circular dependencies
-			// between the modules in the snapshot and the snapshot itself.
-			var snapshotModule []blueprint.Module
-			if c.InVendor() && c.VndkVersion() == actx.DeviceConfig().VndkVersion() {
-				snapshotModule = ctx.AddVariationDependencies(nil, nil, "vendor_snapshot")
-			} else if recoverySnapshotVersion := actx.DeviceConfig().RecoverySnapshotVersion(); recoverySnapshotVersion != "current" && recoverySnapshotVersion != "" && c.InRecovery() {
-				snapshotModule = ctx.AddVariationDependencies(nil, nil, "recovery_snapshot")
-			}
-			if len(snapshotModule) > 0 {
-				snapshot := ctx.OtherModuleProvider(snapshotModule[0], SnapshotInfoProvider).(SnapshotInfo)
-				snapshotInfo = &snapshot
-				// republish the snapshot for use in later mutators on this module
-				ctx.SetProvider(SnapshotInfoProvider, snapshot)
-			} else {
-				snapshotInfo = &SnapshotInfo{}
-			}
-		}
-
-		return *snapshotInfo
-	}
-
-	rewriteSnapshotLib := func(lib string, snapshotMap map[string]string) string {
-		if snapshot, ok := snapshotMap[lib]; ok {
-			return snapshot
-		}
-
-		return lib
-	}
 
 	variantNdkLibs := []string{}
 	variantLateNdkLibs := []string{}
 	if ctx.Os() == android.Android {
-		// rewriteLibs takes a list of names of shared libraries and scans it for three types
-		// of names:
-		//
-		// 1. Name of an NDK library that refers to a prebuilt module.
-		//    For each of these, it adds the name of the prebuilt module (which will be in
-		//    prebuilts/ndk) to the list of nonvariant libs.
-		// 2. Name of an NDK library that refers to an ndk_library module.
-		//    For each of these, it adds the name of the ndk_library module to the list of
-		//    variant libs.
-		// 3. Anything else (so anything that isn't an NDK library).
-		//    It adds these to the nonvariantLibs list.
-		//
-		// The caller can then know to add the variantLibs dependencies differently from the
-		// nonvariantLibs
-
-		rewriteLibs := func(list []string) (nonvariantLibs []string, variantLibs []string) {
-			variantLibs = []string{}
-			nonvariantLibs = []string{}
-			for _, entry := range list {
-				// strip #version suffix out
-				name, _ := StubsLibNameAndVersion(entry)
-				if c.InRecovery() {
-					nonvariantLibs = append(nonvariantLibs, rewriteSnapshotLib(entry, getSnapshot().SharedLibs))
-				} else if ctx.useSdk() && inList(name, *getNDKKnownLibs(ctx.Config())) {
-					variantLibs = append(variantLibs, name+ndkLibrarySuffix)
-				} else if ctx.useVndk() {
-					nonvariantLibs = append(nonvariantLibs, rewriteSnapshotLib(entry, getSnapshot().SharedLibs))
-				} else {
-					// put name#version back
-					nonvariantLibs = append(nonvariantLibs, entry)
-				}
-			}
-			return nonvariantLibs, variantLibs
-		}
-
-		deps.SharedLibs, variantNdkLibs = rewriteLibs(deps.SharedLibs)
-		deps.LateSharedLibs, variantLateNdkLibs = rewriteLibs(deps.LateSharedLibs)
-		deps.ReexportSharedLibHeaders, _ = rewriteLibs(deps.ReexportSharedLibHeaders)
+		deps.SharedLibs, variantNdkLibs = RewriteLibs(c, &snapshotInfo, actx, ctx.Config(), deps.SharedLibs)
+		deps.LateSharedLibs, variantLateNdkLibs = RewriteLibs(c, &snapshotInfo, actx, ctx.Config(), deps.LateSharedLibs)
+		deps.ReexportSharedLibHeaders, _ = RewriteLibs(c, &snapshotInfo, actx, ctx.Config(), deps.ReexportSharedLibHeaders)
 
 		for idx, lib := range deps.RuntimeLibs {
-			deps.RuntimeLibs[idx] = rewriteSnapshotLib(lib, getSnapshot().SharedLibs)
+			deps.RuntimeLibs[idx] = RewriteSnapshotLib(lib, GetSnapshot(c, &snapshotInfo, actx).SharedLibs)
 		}
 	}
 
@@ -2102,7 +2107,7 @@
 			depTag.reexportFlags = true
 		}
 
-		lib = rewriteSnapshotLib(lib, getSnapshot().HeaderLibs)
+		lib = RewriteSnapshotLib(lib, GetSnapshot(c, &snapshotInfo, actx).HeaderLibs)
 
 		if c.IsStubs() {
 			actx.AddFarVariationDependencies(append(ctx.Target().Variations(), c.ImageVariation()),
@@ -2112,6 +2117,15 @@
 		}
 	}
 
+	if c.isNDKStubLibrary() {
+		// NDK stubs depend on their implementation because the ABI dumps are
+		// generated from the implementation library.
+		actx.AddFarVariationDependencies(append(ctx.Target().Variations(),
+			c.ImageVariation(),
+			blueprint.Variation{Mutator: "link", Variation: "shared"},
+		), stubImplementation, c.BaseModuleName())
+	}
+
 	// sysprop_library has to support both C++ and Java. So sysprop_library internally creates one
 	// C++ implementation library and one Java implementation library. When a module links against
 	// sysprop_library, the C++ implementation library has to be linked. syspropImplLibraries is a
@@ -2125,7 +2139,7 @@
 			lib = impl
 		}
 
-		lib = rewriteSnapshotLib(lib, getSnapshot().StaticLibs)
+		lib = RewriteSnapshotLib(lib, GetSnapshot(c, &snapshotInfo, actx).StaticLibs)
 
 		actx.AddVariationDependencies([]blueprint.Variation{
 			{Mutator: "link", Variation: "static"},
@@ -2145,7 +2159,7 @@
 			lib = impl
 		}
 
-		lib = rewriteSnapshotLib(lib, getSnapshot().StaticLibs)
+		lib = RewriteSnapshotLib(lib, GetSnapshot(c, &snapshotInfo, actx).StaticLibs)
 
 		actx.AddVariationDependencies([]blueprint.Variation{
 			{Mutator: "link", Variation: "static"},
@@ -2159,14 +2173,14 @@
 		depTag := libraryDependencyTag{Kind: staticLibraryDependency, staticUnwinder: true}
 		actx.AddVariationDependencies([]blueprint.Variation{
 			{Mutator: "link", Variation: "static"},
-		}, depTag, rewriteSnapshotLib(staticUnwinder(actx), getSnapshot().StaticLibs))
+		}, depTag, RewriteSnapshotLib(staticUnwinder(actx), GetSnapshot(c, &snapshotInfo, actx).StaticLibs))
 	}
 
 	for _, lib := range deps.LateStaticLibs {
 		depTag := libraryDependencyTag{Kind: staticLibraryDependency, Order: lateLibraryDependency}
 		actx.AddVariationDependencies([]blueprint.Variation{
 			{Mutator: "link", Variation: "static"},
-		}, depTag, rewriteSnapshotLib(lib, getSnapshot().StaticLibs))
+		}, depTag, RewriteSnapshotLib(lib, GetSnapshot(c, &snapshotInfo, actx).StaticLibs))
 	}
 
 	// shared lib names without the #version suffix
@@ -2191,7 +2205,7 @@
 		variations := []blueprint.Variation{
 			{Mutator: "link", Variation: "shared"},
 		}
-		c.addSharedLibDependenciesWithVersions(ctx, variations, depTag, name, version, false)
+		AddSharedLibDependenciesWithVersions(ctx, c, variations, depTag, name, version, false)
 	}
 
 	for _, lib := range deps.LateSharedLibs {
@@ -2205,7 +2219,7 @@
 		variations := []blueprint.Variation{
 			{Mutator: "link", Variation: "shared"},
 		}
-		c.addSharedLibDependenciesWithVersions(ctx, variations, depTag, lib, "", false)
+		AddSharedLibDependenciesWithVersions(ctx, c, variations, depTag, lib, "", false)
 	}
 
 	actx.AddVariationDependencies([]blueprint.Variation{
@@ -2230,11 +2244,11 @@
 	actx.AddVariationDependencies(crtVariations, objDepTag, deps.ObjFiles...)
 	if deps.CrtBegin != "" {
 		actx.AddVariationDependencies(crtVariations, CrtBeginDepTag,
-			rewriteSnapshotLib(deps.CrtBegin, getSnapshot().Objects))
+			RewriteSnapshotLib(deps.CrtBegin, GetSnapshot(c, &snapshotInfo, actx).Objects))
 	}
 	if deps.CrtEnd != "" {
 		actx.AddVariationDependencies(crtVariations, CrtEndDepTag,
-			rewriteSnapshotLib(deps.CrtEnd, getSnapshot().Objects))
+			RewriteSnapshotLib(deps.CrtEnd, GetSnapshot(c, &snapshotInfo, actx).Objects))
 	}
 	if deps.LinkerFlagsFile != "" {
 		actx.AddDependency(c, linkerFlagsDepTag, deps.LinkerFlagsFile)
@@ -2837,8 +2851,8 @@
 				// they merely serve as Make dependencies and do not affect this lib itself.
 				c.Properties.AndroidMkSharedLibs = append(
 					c.Properties.AndroidMkSharedLibs, makeLibName)
-				// Record baseLibName for snapshots.
-				c.Properties.SnapshotSharedLibs = append(c.Properties.SnapshotSharedLibs, baseLibName(depName))
+				// Record BaseLibName for snapshots.
+				c.Properties.SnapshotSharedLibs = append(c.Properties.SnapshotSharedLibs, BaseLibName(depName))
 			case libDepTag.static():
 				if libDepTag.wholeStatic {
 					c.Properties.AndroidMkWholeStaticLibs = append(
@@ -2855,8 +2869,8 @@
 			case runtimeDepTag:
 				c.Properties.AndroidMkRuntimeLibs = append(
 					c.Properties.AndroidMkRuntimeLibs, MakeLibName(ctx, c, ccDep, depName)+libDepTag.makeSuffix)
-				// Record baseLibName for snapshots.
-				c.Properties.SnapshotRuntimeLibs = append(c.Properties.SnapshotRuntimeLibs, baseLibName(depName))
+				// Record BaseLibName for snapshots.
+				c.Properties.SnapshotRuntimeLibs = append(c.Properties.SnapshotRuntimeLibs, BaseLibName(depName))
 			case objDepTag:
 				depPaths.Objs.objFiles = append(depPaths.Objs.objFiles, linkFile.Path())
 			case CrtBeginDepTag:
@@ -2924,8 +2938,8 @@
 	return orderedStaticPaths, transitiveStaticLibs
 }
 
-// baseLibName trims known prefixes and suffixes
-func baseLibName(depName string) string {
+// BaseLibName trims known prefixes and suffixes
+func BaseLibName(depName string) string {
 	libName := strings.TrimSuffix(depName, llndkLibrarySuffix)
 	libName = strings.TrimSuffix(libName, vendorPublicLibrarySuffix)
 	libName = android.RemoveOptionalPrebuiltPrefix(libName)
@@ -2933,7 +2947,7 @@
 }
 
 func MakeLibName(ctx android.ModuleContext, c LinkableInterface, ccDep LinkableInterface, depName string) string {
-	libName := baseLibName(depName)
+	libName := BaseLibName(depName)
 	ccDepModule, _ := ccDep.(*Module)
 	isLLndk := ccDepModule != nil && ccDepModule.IsLlndk()
 	nonSystemVariantsExist := ccDep.HasNonSystemVariants() || isLLndk
@@ -2941,10 +2955,10 @@
 	if ccDepModule != nil {
 		// TODO(ivanlozano) Support snapshots for Rust-produced C library variants.
 		// Use base module name for snapshots when exporting to Makefile.
-		if snapshotPrebuilt, ok := ccDepModule.linker.(snapshotInterface); ok {
+		if snapshotPrebuilt, ok := ccDepModule.linker.(SnapshotInterface); ok {
 			baseName := ccDepModule.BaseModuleName()
 
-			return baseName + snapshotPrebuilt.snapshotAndroidMkSuffix()
+			return baseName + snapshotPrebuilt.SnapshotAndroidMkSuffix()
 		}
 	}
 
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 7fc044d..e0fae5a 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -396,50 +396,6 @@
 	}
 }
 
-func checkSnapshotIncludeExclude(t *testing.T, ctx *android.TestContext, singleton android.TestingSingleton, moduleName, snapshotFilename, subDir, variant string, include bool, fake bool) {
-	t.Helper()
-	mod := ctx.ModuleForTests(moduleName, variant)
-	outputFiles := mod.OutputFiles(t, "")
-	if len(outputFiles) != 1 {
-		t.Errorf("%q must have single output\n", moduleName)
-		return
-	}
-	snapshotPath := filepath.Join(subDir, snapshotFilename)
-
-	if include {
-		out := singleton.Output(snapshotPath)
-		if fake {
-			if out.Rule == nil {
-				t.Errorf("Missing rule for module %q output file %q", moduleName, outputFiles[0])
-			}
-		} else {
-			if out.Input.String() != outputFiles[0].String() {
-				t.Errorf("The input of snapshot %q must be %q, but %q", moduleName, out.Input.String(), outputFiles[0])
-			}
-		}
-	} else {
-		out := singleton.MaybeOutput(snapshotPath)
-		if out.Rule != nil {
-			t.Errorf("There must be no rule for module %q output file %q", moduleName, outputFiles[0])
-		}
-	}
-}
-
-func checkSnapshot(t *testing.T, ctx *android.TestContext, singleton android.TestingSingleton, moduleName, snapshotFilename, subDir, variant string) {
-	t.Helper()
-	checkSnapshotIncludeExclude(t, ctx, singleton, moduleName, snapshotFilename, subDir, variant, true, false)
-}
-
-func checkSnapshotExclude(t *testing.T, ctx *android.TestContext, singleton android.TestingSingleton, moduleName, snapshotFilename, subDir, variant string) {
-	t.Helper()
-	checkSnapshotIncludeExclude(t, ctx, singleton, moduleName, snapshotFilename, subDir, variant, false, false)
-}
-
-func checkSnapshotRule(t *testing.T, ctx *android.TestContext, singleton android.TestingSingleton, moduleName, snapshotFilename, subDir, variant string) {
-	t.Helper()
-	checkSnapshotIncludeExclude(t, ctx, singleton, moduleName, snapshotFilename, subDir, variant, true, true)
-}
-
 func checkWriteFileOutput(t *testing.T, params android.TestingBuildParams, expected []string) {
 	t.Helper()
 	content := android.ContentFromFileRuleForTests(t, params)
@@ -631,21 +587,21 @@
 
 	snapshotSingleton := ctx.SingletonForTests("vndk-snapshot")
 
-	checkSnapshot(t, ctx, snapshotSingleton, "libvndk", "libvndk.so", vndkCoreLibPath, variant)
-	checkSnapshot(t, ctx, snapshotSingleton, "libvndk", "libvndk.so", vndkCoreLib2ndPath, variant2nd)
-	checkSnapshot(t, ctx, snapshotSingleton, "libvndk_product", "libvndk_product.so", vndkCoreLibPath, variant)
-	checkSnapshot(t, ctx, snapshotSingleton, "libvndk_product", "libvndk_product.so", vndkCoreLib2ndPath, variant2nd)
-	checkSnapshot(t, ctx, snapshotSingleton, "libvndk_sp", "libvndk_sp-x.so", vndkSpLibPath, variant)
-	checkSnapshot(t, ctx, snapshotSingleton, "libvndk_sp", "libvndk_sp-x.so", vndkSpLib2ndPath, variant2nd)
-	checkSnapshot(t, ctx, snapshotSingleton, "libllndk", "libllndk.so", llndkLibPath, variant)
-	checkSnapshot(t, ctx, snapshotSingleton, "libllndk", "libllndk.so", llndkLib2ndPath, variant2nd)
+	CheckSnapshot(t, ctx, snapshotSingleton, "libvndk", "libvndk.so", vndkCoreLibPath, variant)
+	CheckSnapshot(t, ctx, snapshotSingleton, "libvndk", "libvndk.so", vndkCoreLib2ndPath, variant2nd)
+	CheckSnapshot(t, ctx, snapshotSingleton, "libvndk_product", "libvndk_product.so", vndkCoreLibPath, variant)
+	CheckSnapshot(t, ctx, snapshotSingleton, "libvndk_product", "libvndk_product.so", vndkCoreLib2ndPath, variant2nd)
+	CheckSnapshot(t, ctx, snapshotSingleton, "libvndk_sp", "libvndk_sp-x.so", vndkSpLibPath, variant)
+	CheckSnapshot(t, ctx, snapshotSingleton, "libvndk_sp", "libvndk_sp-x.so", vndkSpLib2ndPath, variant2nd)
+	CheckSnapshot(t, ctx, snapshotSingleton, "libllndk", "libllndk.so", llndkLibPath, variant)
+	CheckSnapshot(t, ctx, snapshotSingleton, "libllndk", "libllndk.so", llndkLib2ndPath, variant2nd)
 
 	snapshotConfigsPath := filepath.Join(snapshotVariantPath, "configs")
-	checkSnapshot(t, ctx, snapshotSingleton, "llndk.libraries.txt", "llndk.libraries.txt", snapshotConfigsPath, "")
-	checkSnapshot(t, ctx, snapshotSingleton, "vndkcore.libraries.txt", "vndkcore.libraries.txt", snapshotConfigsPath, "")
-	checkSnapshot(t, ctx, snapshotSingleton, "vndksp.libraries.txt", "vndksp.libraries.txt", snapshotConfigsPath, "")
-	checkSnapshot(t, ctx, snapshotSingleton, "vndkprivate.libraries.txt", "vndkprivate.libraries.txt", snapshotConfigsPath, "")
-	checkSnapshot(t, ctx, snapshotSingleton, "vndkproduct.libraries.txt", "vndkproduct.libraries.txt", snapshotConfigsPath, "")
+	CheckSnapshot(t, ctx, snapshotSingleton, "llndk.libraries.txt", "llndk.libraries.txt", snapshotConfigsPath, "")
+	CheckSnapshot(t, ctx, snapshotSingleton, "vndkcore.libraries.txt", "vndkcore.libraries.txt", snapshotConfigsPath, "")
+	CheckSnapshot(t, ctx, snapshotSingleton, "vndksp.libraries.txt", "vndksp.libraries.txt", snapshotConfigsPath, "")
+	CheckSnapshot(t, ctx, snapshotSingleton, "vndkprivate.libraries.txt", "vndkprivate.libraries.txt", snapshotConfigsPath, "")
+	CheckSnapshot(t, ctx, snapshotSingleton, "vndkproduct.libraries.txt", "vndkproduct.libraries.txt", snapshotConfigsPath, "")
 
 	checkVndkOutput(t, ctx, "vndk/vndk.libraries.txt", []string{
 		"LLNDK: libc.so",
@@ -2643,15 +2599,6 @@
 	return modulesInOrder, allDeps
 }
 
-func getOutputPaths(ctx *android.TestContext, variant string, moduleNames []string) (paths android.Paths) {
-	for _, moduleName := range moduleNames {
-		module := ctx.ModuleForTests(moduleName, variant).Module().(*Module)
-		output := module.outputFile.Path().RelativeToTop()
-		paths = append(paths, output)
-	}
-	return paths
-}
-
 func TestStaticLibDepReordering(t *testing.T) {
 	ctx := testCc(t, `
 	cc_library {
@@ -2679,7 +2626,7 @@
 	moduleA := ctx.ModuleForTests("a", variant).Module().(*Module)
 	actual := ctx.ModuleProvider(moduleA, StaticLibraryInfoProvider).(StaticLibraryInfo).
 		TransitiveStaticLibrariesForOrdering.ToList().RelativeToTop()
-	expected := getOutputPaths(ctx, variant, []string{"a", "c", "b", "d"})
+	expected := GetOutputPaths(ctx, variant, []string{"a", "c", "b", "d"})
 
 	if !reflect.DeepEqual(actual, expected) {
 		t.Errorf("staticDeps orderings were not propagated correctly"+
@@ -2714,7 +2661,7 @@
 	moduleA := ctx.ModuleForTests("a", variant).Module().(*Module)
 	actual := ctx.ModuleProvider(moduleA, StaticLibraryInfoProvider).(StaticLibraryInfo).
 		TransitiveStaticLibrariesForOrdering.ToList().RelativeToTop()
-	expected := getOutputPaths(ctx, variant, []string{"a", "c", "b"})
+	expected := GetOutputPaths(ctx, variant, []string{"a", "c", "b"})
 
 	if !reflect.DeepEqual(actual, expected) {
 		t.Errorf("staticDeps orderings did not account for shared libs"+
@@ -3357,7 +3304,7 @@
 
 	mybin := ctx.ModuleForTests("mybin", "android_arm64_armv8-a").Rule("ld")
 	actual := mybin.Implicits[:2]
-	expected := getOutputPaths(ctx, "android_arm64_armv8-a_static", []string{"libfooB", "libfooC"})
+	expected := GetOutputPaths(ctx, "android_arm64_armv8-a_static", []string{"libfooB", "libfooC"})
 
 	if !reflect.DeepEqual(actual, expected) {
 		t.Errorf("staticDeps orderings were not propagated correctly"+
diff --git a/cc/config/global.go b/cc/config/global.go
index d458311..24e10a4 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -294,12 +294,3 @@
 }
 
 var HostPrebuiltTag = pctx.VariableConfigMethod("HostPrebuiltTag", android.Config.PrebuiltOS)
-
-func envOverrideFunc(envVar, defaultVal string) func(ctx android.PackageVarContext) string {
-	return func(ctx android.PackageVarContext) string {
-		if override := ctx.Config().Getenv(envVar); override != "" {
-			return override
-		}
-		return defaultVal
-	}
-}
diff --git a/cc/genrule.go b/cc/genrule.go
index 82d7205..b0efc6c 100644
--- a/cc/genrule.go
+++ b/cc/genrule.go
@@ -103,7 +103,7 @@
 		// If not, we assume modules under proprietary paths are compatible for
 		// BOARD_VNDK_VERSION. The other modules are regarded as AOSP, that is
 		// PLATFORM_VNDK_VERSION.
-		if vndkVersion == "current" || !isVendorProprietaryModule(ctx) {
+		if vndkVersion == "current" || !IsVendorProprietaryModule(ctx) {
 			variants = append(variants, VendorVariationPrefix+ctx.DeviceConfig().PlatformVndkVersion())
 		} else {
 			variants = append(variants, VendorVariationPrefix+vndkVersion)
diff --git a/cc/image.go b/cc/image.go
index c6b209f..c9c0e63 100644
--- a/cc/image.go
+++ b/cc/image.go
@@ -365,8 +365,8 @@
 }
 
 func (m *Module) SnapshotVersion(mctx android.BaseModuleContext) string {
-	if snapshot, ok := m.linker.(snapshotInterface); ok {
-		return snapshot.version()
+	if snapshot, ok := m.linker.(SnapshotInterface); ok {
+		return snapshot.Version()
 	} else {
 		mctx.ModuleErrorf("version is unknown for snapshot prebuilt")
 		// Should we be panicking here instead?
@@ -496,7 +496,7 @@
 		// BOARD_VNDK_VERSION. The other modules are regarded as AOSP, or
 		// PLATFORM_VNDK_VERSION.
 		if m.HasVendorVariant() {
-			if isVendorProprietaryModule(mctx) {
+			if IsVendorProprietaryModule(mctx) {
 				vendorVariants = append(vendorVariants, boardVndkVersion)
 			} else {
 				vendorVariants = append(vendorVariants, platformVndkVersion)
@@ -525,7 +525,7 @@
 				platformVndkVersion,
 				boardVndkVersion,
 			)
-		} else if isVendorProprietaryModule(mctx) {
+		} else if IsVendorProprietaryModule(mctx) {
 			vendorVariants = append(vendorVariants, boardVndkVersion)
 		} else {
 			vendorVariants = append(vendorVariants, platformVndkVersion)
diff --git a/cc/library.go b/cc/library.go
index cdac01c..c88c29a 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -111,9 +111,6 @@
 		Check_all_apis *bool
 	}
 
-	// Order symbols in .bss section by their sizes.  Only useful for shared libraries.
-	Sort_bss_symbols_by_size *bool
-
 	// Inject boringssl hash into the shared library.  This is only intended for use by external/boringssl.
 	Inject_bssl_hash *bool `android:"arch_variant"`
 
@@ -305,8 +302,7 @@
 	linkerAttrs := bp2BuildParseLinkerProps(ctx, m)
 	exportedIncludes := bp2BuildParseExportedIncludes(ctx, m)
 
-	var srcs bazel.LabelListAttribute
-	srcs.Append(compilerAttrs.srcs)
+	srcs := compilerAttrs.srcs
 
 	attrs := &bazelCcLibraryAttributes{
 		Srcs:    srcs,
@@ -363,6 +359,7 @@
 		staticLibrarySdkMemberType,
 		staticAndSharedLibrarySdkMemberType,
 	}
+	module.bazelHandler = &ccLibraryBazelHandler{module: module}
 	return module.Init()
 }
 
@@ -371,7 +368,7 @@
 	module, library := NewLibrary(android.HostAndDeviceSupported)
 	library.BuildOnlyStatic()
 	module.sdkMemberTypes = []android.SdkMemberType{staticLibrarySdkMemberType}
-	module.bazelHandler = &staticLibraryBazelHandler{module: module}
+	module.bazelHandler = &ccLibraryBazelHandler{module: module}
 	return module.Init()
 }
 
@@ -556,13 +553,17 @@
 	collectedSnapshotHeaders android.Paths
 }
 
-type staticLibraryBazelHandler struct {
+type ccLibraryBazelHandler struct {
 	bazelHandler
 
 	module *Module
 }
 
-func (handler *staticLibraryBazelHandler) generateBazelBuildActions(ctx android.ModuleContext, label string) bool {
+func (handler *ccLibraryBazelHandler) generateBazelBuildActions(ctx android.ModuleContext, label string) bool {
+	if !handler.module.static() {
+		// TODO(cparsons): Support shared libraries.
+		return false
+	}
 	bazelCtx := ctx.Config().BazelContext
 	ccInfo, ok, err := bazelCtx.GetCcInfo(label, ctx.Arch().ArchType)
 	if err != nil {
@@ -572,18 +573,13 @@
 	if !ok {
 		return ok
 	}
-	outputPaths := ccInfo.OutputFiles
+	rootStaticArchives := ccInfo.RootStaticArchives
 	objPaths := ccInfo.CcObjectFiles
-	if len(outputPaths) > 1 {
-		// TODO(cparsons): This is actually expected behavior for static libraries with no srcs.
-		// We should support this.
-		ctx.ModuleErrorf("expected at most one output file for '%s', but got %s", label, objPaths)
+	if len(rootStaticArchives) != 1 {
+		ctx.ModuleErrorf("expected exactly one root archive file for '%s', but got %s", label, rootStaticArchives)
 		return false
-	} else if len(outputPaths) == 0 {
-		handler.module.outputFile = android.OptionalPath{}
-		return true
 	}
-	outputFilePath := android.PathForBazelOut(ctx, outputPaths[0])
+	outputFilePath := android.PathForBazelOut(ctx, rootStaticArchives[0])
 	handler.module.outputFile = android.OptionalPathForPath(outputFilePath)
 
 	objFiles := make(android.Paths, len(objPaths))
@@ -619,20 +615,14 @@
 	return ok
 }
 
-// collectHeadersForSnapshot collects all exported headers from library.
-// It globs header files in the source tree for exported include directories,
-// and tracks generated header files separately.
-//
-// This is to be called from GenerateAndroidBuildActions, and then collected
-// header files can be retrieved by snapshotHeaders().
-func (l *libraryDecorator) collectHeadersForSnapshot(ctx android.ModuleContext) {
+func GlobHeadersForSnapshot(ctx android.ModuleContext, paths android.Paths) android.Paths {
 	ret := android.Paths{}
 
 	// Headers in the source tree should be globbed. On the contrast, generated headers
 	// can't be globbed, and they should be manually collected.
 	// So, we first filter out intermediate directories (which contains generated headers)
 	// from exported directories, and then glob headers under remaining directories.
-	for _, path := range append(android.CopyOfPaths(l.flagExporter.dirs), l.flagExporter.systemDirs...) {
+	for _, path := range paths {
 		dir := path.String()
 		// Skip if dir is for generated headers
 		if strings.HasPrefix(dir, android.PathForOutput(ctx).String()) {
@@ -648,7 +638,7 @@
 				glob, err := ctx.GlobWithDeps("external/eigen/"+subdir+"/**/*", nil)
 				if err != nil {
 					ctx.ModuleErrorf("glob failed: %#v", err)
-					return
+					return nil
 				}
 				for _, header := range glob {
 					if strings.HasSuffix(header, "/") {
@@ -666,7 +656,7 @@
 		glob, err := ctx.GlobWithDeps(dir+"/**/*", nil)
 		if err != nil {
 			ctx.ModuleErrorf("glob failed: %#v", err)
-			return
+			return nil
 		}
 		isLibcxx := strings.HasPrefix(dir, "external/libcxx/include")
 		for _, header := range glob {
@@ -679,7 +669,7 @@
 			} else {
 				// Filter out only the files with extensions that are headers.
 				found := false
-				for _, ext := range headerExts {
+				for _, ext := range HeaderExts {
 					if strings.HasSuffix(header, ext) {
 						found = true
 						break
@@ -692,15 +682,38 @@
 			ret = append(ret, android.PathForSource(ctx, header))
 		}
 	}
+	return ret
+}
 
-	// Collect generated headers
-	for _, header := range append(android.CopyOfPaths(l.flagExporter.headers), l.flagExporter.deps...) {
+func GlobGeneratedHeadersForSnapshot(ctx android.ModuleContext, paths android.Paths) android.Paths {
+	ret := android.Paths{}
+	for _, header := range paths {
 		// TODO(b/148123511): remove exportedDeps after cleaning up genrule
 		if strings.HasSuffix(header.Base(), "-phony") {
 			continue
 		}
 		ret = append(ret, header)
 	}
+	return ret
+}
+
+// collectHeadersForSnapshot collects all exported headers from library.
+// It globs header files in the source tree for exported include directories,
+// and tracks generated header files separately.
+//
+// This is to be called from GenerateAndroidBuildActions, and then collected
+// header files can be retrieved by snapshotHeaders().
+func (l *libraryDecorator) collectHeadersForSnapshot(ctx android.ModuleContext) {
+	ret := android.Paths{}
+
+	// Headers in the source tree should be globbed. On the contrast, generated headers
+	// can't be globbed, and they should be manually collected.
+	// So, we first filter out intermediate directories (which contains generated headers)
+	// from exported directories, and then glob headers under remaining directories.
+	ret = append(ret, GlobHeadersForSnapshot(ctx, append(android.CopyOfPaths(l.flagExporter.dirs), l.flagExporter.systemDirs...))...)
+
+	// Collect generated headers
+	ret = append(ret, GlobGeneratedHeadersForSnapshot(ctx, append(android.CopyOfPaths(l.flagExporter.headers), l.flagExporter.deps...))...)
 
 	l.collectedSnapshotHeaders = ret
 }
@@ -849,16 +862,23 @@
 		if library.stubsVersion() != "" {
 			vndkVer = library.stubsVersion()
 		}
-		objs, versionScript := compileStubLibrary(ctx, flags, String(library.Properties.Llndk.Symbol_file), vndkVer, "--llndk")
+		nativeAbiResult := parseNativeAbiDefinition(ctx,
+			String(library.Properties.Llndk.Symbol_file),
+			android.ApiLevelOrPanic(ctx, vndkVer), "--llndk")
+		objs := compileStubLibrary(ctx, flags, nativeAbiResult.stubSrc)
 		if !Bool(library.Properties.Llndk.Unversioned) {
-			library.versionScriptPath = android.OptionalPathForPath(versionScript)
+			library.versionScriptPath = android.OptionalPathForPath(
+				nativeAbiResult.versionScript)
 		}
 		return objs
 	}
 	if ctx.IsVendorPublicLibrary() {
-		objs, versionScript := compileStubLibrary(ctx, flags, String(library.Properties.Vendor_public_library.Symbol_file), "current", "")
+		nativeAbiResult := parseNativeAbiDefinition(ctx,
+			String(library.Properties.Vendor_public_library.Symbol_file),
+			android.FutureApiLevel, "")
+		objs := compileStubLibrary(ctx, flags, nativeAbiResult.stubSrc)
 		if !Bool(library.Properties.Vendor_public_library.Unversioned) {
-			library.versionScriptPath = android.OptionalPathForPath(versionScript)
+			library.versionScriptPath = android.OptionalPathForPath(nativeAbiResult.versionScript)
 		}
 		return objs
 	}
@@ -868,8 +888,12 @@
 			ctx.PropertyErrorf("symbol_file", "%q doesn't have .map.txt suffix", symbolFile)
 			return Objects{}
 		}
-		objs, versionScript := compileStubLibrary(ctx, flags, String(library.Properties.Stubs.Symbol_file), library.MutatedProperties.StubsVersion, "--apex")
-		library.versionScriptPath = android.OptionalPathForPath(versionScript)
+		nativeAbiResult := parseNativeAbiDefinition(ctx, symbolFile,
+			android.ApiLevelOrPanic(ctx, library.MutatedProperties.StubsVersion),
+			"--apex")
+		objs := compileStubLibrary(ctx, flags, nativeAbiResult.stubSrc)
+		library.versionScriptPath = android.OptionalPathForPath(
+			nativeAbiResult.versionScript)
 		return objs
 	}
 
@@ -1315,19 +1339,6 @@
 	linkerDeps = append(linkerDeps, deps.SharedLibsDeps...)
 	linkerDeps = append(linkerDeps, deps.LateSharedLibsDeps...)
 	linkerDeps = append(linkerDeps, objs.tidyFiles...)
-
-	if Bool(library.Properties.Sort_bss_symbols_by_size) && !library.buildStubs() {
-		unsortedOutputFile := android.PathForModuleOut(ctx, "unsorted", fileName)
-		transformObjToDynamicBinary(ctx, objs.objFiles, sharedLibs,
-			deps.StaticLibs, deps.LateStaticLibs, deps.WholeStaticLibs,
-			linkerDeps, deps.CrtBegin, deps.CrtEnd, false, builderFlags, unsortedOutputFile, implicitOutputs)
-
-		symbolOrderingFile := android.PathForModuleOut(ctx, "unsorted", fileName+".symbol_order")
-		symbolOrderingFlag := library.baseLinker.sortBssSymbolsBySize(ctx, unsortedOutputFile, symbolOrderingFile, builderFlags)
-		builderFlags.localLdFlags += " " + symbolOrderingFlag
-		linkerDeps = append(linkerDeps, symbolOrderingFile)
-	}
-
 	transformObjToDynamicBinary(ctx, objs.objFiles, sharedLibs,
 		deps.StaticLibs, deps.LateStaticLibs, deps.WholeStaticLibs,
 		linkerDeps, deps.CrtBegin, deps.CrtEnd, false, builderFlags, outputFile, implicitOutputs)
diff --git a/cc/linker.go b/cc/linker.go
index 5bd21ed..1d8c649 100644
--- a/cc/linker.go
+++ b/cc/linker.go
@@ -594,28 +594,3 @@
 		},
 	})
 }
-
-// Rule to generate .bss symbol ordering file.
-
-var (
-	_                   = pctx.SourcePathVariable("genSortedBssSymbolsPath", "build/soong/scripts/gen_sorted_bss_symbols.sh")
-	genSortedBssSymbols = pctx.AndroidStaticRule("gen_sorted_bss_symbols",
-		blueprint.RuleParams{
-			Command:     "CLANG_BIN=${clangBin} $genSortedBssSymbolsPath ${in} ${out}",
-			CommandDeps: []string{"$genSortedBssSymbolsPath", "${clangBin}/llvm-nm"},
-		},
-		"clangBin")
-)
-
-func (linker *baseLinker) sortBssSymbolsBySize(ctx ModuleContext, in android.Path, symbolOrderingFile android.ModuleOutPath, flags builderFlags) string {
-	ctx.Build(pctx, android.BuildParams{
-		Rule:        genSortedBssSymbols,
-		Description: "generate bss symbol order " + symbolOrderingFile.Base(),
-		Output:      symbolOrderingFile,
-		Input:       in,
-		Args: map[string]string{
-			"clangBin": "${config.ClangBin}",
-		},
-	})
-	return "-Wl,--symbol-ordering-file," + symbolOrderingFile.String()
-}
diff --git a/cc/ndk_abi.go b/cc/ndk_abi.go
new file mode 100644
index 0000000..927fa2e
--- /dev/null
+++ b/cc/ndk_abi.go
@@ -0,0 +1,102 @@
+// 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"
+)
+
+func init() {
+	android.RegisterSingletonType("ndk_abi_dump", NdkAbiDumpSingleton)
+	android.RegisterSingletonType("ndk_abi_diff", NdkAbiDiffSingleton)
+}
+
+func getNdkAbiDumpInstallBase(ctx android.PathContext) android.OutputPath {
+	return android.PathForOutput(ctx).Join(ctx, "abi-dumps/ndk")
+}
+
+func getNdkAbiDumpTimestampFile(ctx android.PathContext) android.OutputPath {
+	return android.PathForOutput(ctx, "ndk_abi_dump.timestamp")
+}
+
+func NdkAbiDumpSingleton() android.Singleton {
+	return &ndkAbiDumpSingleton{}
+}
+
+type ndkAbiDumpSingleton struct{}
+
+func (n *ndkAbiDumpSingleton) GenerateBuildActions(ctx android.SingletonContext) {
+	var depPaths android.Paths
+	ctx.VisitAllModules(func(module android.Module) {
+		if !module.Enabled() {
+			return
+		}
+
+		if m, ok := module.(*Module); ok {
+			if installer, ok := m.installer.(*stubDecorator); ok {
+				if canDumpAbi() {
+					depPaths = append(depPaths, installer.abiDumpPath)
+				}
+			}
+		}
+	})
+
+	// `m dump-ndk-abi` will dump the NDK ABI.
+	// `development/tools/ndk/update_ndk_abi.sh` will dump the NDK ABI and
+	// update the golden copies in prebuilts/abi-dumps/ndk.
+	ctx.Build(pctx, android.BuildParams{
+		Rule:      android.Touch,
+		Output:    getNdkAbiDumpTimestampFile(ctx),
+		Implicits: depPaths,
+	})
+
+	ctx.Phony("dump-ndk-abi", getNdkAbiDumpTimestampFile(ctx))
+}
+
+func getNdkAbiDiffTimestampFile(ctx android.PathContext) android.WritablePath {
+	return android.PathForOutput(ctx, "ndk_abi_diff.timestamp")
+}
+
+func NdkAbiDiffSingleton() android.Singleton {
+	return &ndkAbiDiffSingleton{}
+}
+
+type ndkAbiDiffSingleton struct{}
+
+func (n *ndkAbiDiffSingleton) GenerateBuildActions(ctx android.SingletonContext) {
+	var depPaths android.Paths
+	ctx.VisitAllModules(func(module android.Module) {
+		if m, ok := module.(android.Module); ok && !m.Enabled() {
+			return
+		}
+
+		if m, ok := module.(*Module); ok {
+			if installer, ok := m.installer.(*stubDecorator); ok {
+				depPaths = append(depPaths, installer.abiDiffPaths...)
+			}
+		}
+	})
+
+	depPaths = append(depPaths, getNdkAbiDumpTimestampFile(ctx))
+
+	// `m diff-ndk-abi` will diff the NDK ABI.
+	ctx.Build(pctx, android.BuildParams{
+		Rule:      android.Touch,
+		Output:    getNdkAbiDiffTimestampFile(ctx),
+		Implicits: depPaths,
+	})
+
+	ctx.Phony("diff-ndk-abi", getNdkAbiDiffTimestampFile(ctx))
+}
diff --git a/cc/ndk_library.go b/cc/ndk_library.go
index 95d8477..a458380 100644
--- a/cc/ndk_library.go
+++ b/cc/ndk_library.go
@@ -16,6 +16,8 @@
 
 import (
 	"fmt"
+	"path/filepath"
+	"runtime"
 	"strings"
 	"sync"
 
@@ -28,6 +30,9 @@
 func init() {
 	pctx.HostBinToolVariable("ndkStubGenerator", "ndkstubgen")
 	pctx.HostBinToolVariable("ndk_api_coverage_parser", "ndk_api_coverage_parser")
+	pctx.HostBinToolVariable("abidiff", "abidiff")
+	pctx.HostBinToolVariable("abitidy", "abitidy")
+	pctx.HostBinToolVariable("abidw", "abidw")
 }
 
 var (
@@ -44,11 +49,31 @@
 			CommandDeps: []string{"$ndk_api_coverage_parser"},
 		}, "apiMap")
 
+	abidw = pctx.AndroidStaticRule("abidw",
+		blueprint.RuleParams{
+			Command: "$abidw --type-id-style hash --no-corpus-path " +
+				"--no-show-locs --no-comp-dir-path -w $symbolList $in | " +
+				"$abitidy --all -o $out",
+			CommandDeps: []string{"$abitidy", "$abidw"},
+		}, "symbolList")
+
+	abidiff = pctx.AndroidStaticRule("abidiff",
+		blueprint.RuleParams{
+			// Need to create *some* output for ninja. We don't want to use tee
+			// because we don't want to spam the build output with "nothing
+			// changed" messages, so redirect output message to $out, and if
+			// changes were detected print the output and fail.
+			Command:     "$abidiff $args $in > $out || (cat $out && false)",
+			CommandDeps: []string{"$abidiff"},
+		}, "args")
+
 	ndkLibrarySuffix = ".ndk"
 
 	ndkKnownLibsKey = android.NewOnceKey("ndkKnownLibsKey")
 	// protects ndkKnownLibs writes during parallel BeginMutator.
 	ndkKnownLibsLock sync.Mutex
+
+	stubImplementation = dependencyTag{name: "stubImplementation"}
 )
 
 // The First_version and Unversioned_until properties of this struct should not
@@ -89,6 +114,8 @@
 	versionScriptPath     android.ModuleGenPath
 	parsedCoverageXmlPath android.ModuleOutPath
 	installPath           android.Path
+	abiDumpPath           android.OutputPath
+	abiDiffPaths          android.Paths
 
 	apiLevel         android.ApiLevel
 	firstVersion     android.ApiLevel
@@ -123,6 +150,16 @@
 	if !ctx.Module().Enabled() {
 		return nil
 	}
+	if ctx.Os() != android.Android {
+		// These modules are always android.DeviceEnabled only, but
+		// those include Fuchsia devices, which we don't support.
+		ctx.Module().Disable()
+		return nil
+	}
+	if ctx.Target().NativeBridge == android.NativeBridgeEnabled {
+		ctx.Module().Disable()
+		return nil
+	}
 	firstVersion, err := nativeApiLevelFromUser(ctx,
 		String(this.properties.First_version))
 	if err != nil {
@@ -204,30 +241,45 @@
 	return addStubLibraryCompilerFlags(flags)
 }
 
-func compileStubLibrary(ctx ModuleContext, flags Flags, symbolFile, apiLevel, genstubFlags string) (Objects, android.ModuleGenPath) {
-	arch := ctx.Arch().ArchType.String()
+type ndkApiOutputs struct {
+	stubSrc       android.ModuleGenPath
+	versionScript android.ModuleGenPath
+	symbolList    android.ModuleGenPath
+}
+
+func parseNativeAbiDefinition(ctx ModuleContext, symbolFile string,
+	apiLevel android.ApiLevel, genstubFlags string) ndkApiOutputs {
 
 	stubSrcPath := android.PathForModuleGen(ctx, "stub.c")
 	versionScriptPath := android.PathForModuleGen(ctx, "stub.map")
 	symbolFilePath := android.PathForModuleSrc(ctx, symbolFile)
+	symbolListPath := android.PathForModuleGen(ctx, "abi_symbol_list.txt")
 	apiLevelsJson := android.GetApiLevelsJson(ctx)
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        genStubSrc,
 		Description: "generate stubs " + symbolFilePath.Rel(),
-		Outputs:     []android.WritablePath{stubSrcPath, versionScriptPath},
-		Input:       symbolFilePath,
-		Implicits:   []android.Path{apiLevelsJson},
+		Outputs: []android.WritablePath{stubSrcPath, versionScriptPath,
+			symbolListPath},
+		Input:     symbolFilePath,
+		Implicits: []android.Path{apiLevelsJson},
 		Args: map[string]string{
-			"arch":     arch,
-			"apiLevel": apiLevel,
+			"arch":     ctx.Arch().ArchType.String(),
+			"apiLevel": apiLevel.String(),
 			"apiMap":   apiLevelsJson.String(),
 			"flags":    genstubFlags,
 		},
 	})
 
-	subdir := ""
-	srcs := []android.Path{stubSrcPath}
-	return compileObjs(ctx, flagsToBuilderFlags(flags), subdir, srcs, nil, nil), versionScriptPath
+	return ndkApiOutputs{
+		stubSrc:       stubSrcPath,
+		versionScript: versionScriptPath,
+		symbolList:    symbolListPath,
+	}
+}
+
+func compileStubLibrary(ctx ModuleContext, flags Flags, src android.Path) Objects {
+	return compileObjs(ctx, flagsToBuilderFlags(flags), "",
+		android.Paths{src}, nil, nil)
 }
 
 func parseSymbolFileForCoverage(ctx ModuleContext, symbolFile string) android.ModuleOutPath {
@@ -248,6 +300,140 @@
 	return parsedApiCoveragePath
 }
 
+func (this *stubDecorator) findImplementationLibrary(ctx ModuleContext) android.Path {
+	dep := ctx.GetDirectDepWithTag(strings.TrimSuffix(ctx.ModuleName(), ndkLibrarySuffix),
+		stubImplementation)
+	if dep == nil {
+		ctx.ModuleErrorf("Could not find implementation for stub")
+		return nil
+	}
+	impl, ok := dep.(*Module)
+	if !ok {
+		ctx.ModuleErrorf("Implementation for stub is not correct module type")
+	}
+	output := impl.UnstrippedOutputFile()
+	if output == nil {
+		ctx.ModuleErrorf("implementation module (%s) has no output", impl)
+		return nil
+	}
+
+	return output
+}
+
+func (this *stubDecorator) libraryName(ctx ModuleContext) string {
+	return strings.TrimSuffix(ctx.ModuleName(), ndkLibrarySuffix)
+}
+
+func (this *stubDecorator) findPrebuiltAbiDump(ctx ModuleContext,
+	apiLevel android.ApiLevel) android.OptionalPath {
+
+	subpath := filepath.Join("prebuilts/abi-dumps/ndk", apiLevel.String(),
+		ctx.Arch().ArchType.String(), this.libraryName(ctx), "abi.xml")
+	return android.ExistentPathForSource(ctx, subpath)
+}
+
+// Feature flag.
+func canDumpAbi() bool {
+	return runtime.GOOS != "darwin"
+}
+
+// Feature flag to disable diffing against prebuilts.
+func canDiffAbi() bool {
+	return false
+}
+
+func (this *stubDecorator) dumpAbi(ctx ModuleContext, symbolList android.Path) {
+	implementationLibrary := this.findImplementationLibrary(ctx)
+	this.abiDumpPath = getNdkAbiDumpInstallBase(ctx).Join(ctx,
+		this.apiLevel.String(), ctx.Arch().ArchType.String(),
+		this.libraryName(ctx), "abi.xml")
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        abidw,
+		Description: fmt.Sprintf("abidw %s", implementationLibrary),
+		Output:      this.abiDumpPath,
+		Input:       implementationLibrary,
+		Implicit:    symbolList,
+		Args: map[string]string{
+			"symbolList": symbolList.String(),
+		},
+	})
+}
+
+func findNextApiLevel(ctx ModuleContext, apiLevel android.ApiLevel) *android.ApiLevel {
+	apiLevels := append(ctx.Config().AllSupportedApiLevels(),
+		android.FutureApiLevel)
+	for _, api := range apiLevels {
+		if api.GreaterThan(apiLevel) {
+			return &api
+		}
+	}
+	return nil
+}
+
+func (this *stubDecorator) diffAbi(ctx ModuleContext) {
+	missingPrebuiltError := fmt.Sprintf(
+		"Did not find prebuilt ABI dump for %q. Generate with "+
+			"//development/tools/ndk/update_ndk_abi.sh.", this.libraryName(ctx))
+
+	// Catch any ABI changes compared to the checked-in definition of this API
+	// level.
+	abiDiffPath := android.PathForModuleOut(ctx, "abidiff.timestamp")
+	prebuiltAbiDump := this.findPrebuiltAbiDump(ctx, this.apiLevel)
+	if !prebuiltAbiDump.Valid() {
+		ctx.Build(pctx, android.BuildParams{
+			Rule:   android.ErrorRule,
+			Output: abiDiffPath,
+			Args: map[string]string{
+				"error": missingPrebuiltError,
+			},
+		})
+	} else {
+		ctx.Build(pctx, android.BuildParams{
+			Rule: abidiff,
+			Description: fmt.Sprintf("abidiff %s %s", prebuiltAbiDump,
+				this.abiDumpPath),
+			Output: abiDiffPath,
+			Inputs: android.Paths{prebuiltAbiDump.Path(), this.abiDumpPath},
+		})
+	}
+	this.abiDiffPaths = append(this.abiDiffPaths, abiDiffPath)
+
+	// Also ensure that the ABI of the next API level (if there is one) matches
+	// this API level. *New* ABI is allowed, but any changes to APIs that exist
+	// in this API level are disallowed.
+	if !this.apiLevel.IsCurrent() {
+		nextApiLevel := findNextApiLevel(ctx, this.apiLevel)
+		if nextApiLevel == nil {
+			panic(fmt.Errorf("could not determine which API level follows "+
+				"non-current API level %s", this.apiLevel))
+		}
+		nextAbiDiffPath := android.PathForModuleOut(ctx,
+			"abidiff_next.timestamp")
+		nextAbiDump := this.findPrebuiltAbiDump(ctx, *nextApiLevel)
+		if !nextAbiDump.Valid() {
+			ctx.Build(pctx, android.BuildParams{
+				Rule:   android.ErrorRule,
+				Output: nextAbiDiffPath,
+				Args: map[string]string{
+					"error": missingPrebuiltError,
+				},
+			})
+		} else {
+			ctx.Build(pctx, android.BuildParams{
+				Rule: abidiff,
+				Description: fmt.Sprintf("abidiff %s %s", this.abiDumpPath,
+					nextAbiDump),
+				Output: nextAbiDiffPath,
+				Inputs: android.Paths{this.abiDumpPath, nextAbiDump.Path()},
+				Args: map[string]string{
+					"args": "--no-added-syms",
+				},
+			})
+		}
+		this.abiDiffPaths = append(this.abiDiffPaths, nextAbiDiffPath)
+	}
+}
+
 func (c *stubDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) Objects {
 	if !strings.HasSuffix(String(c.properties.Symbol_file), ".map.txt") {
 		ctx.PropertyErrorf("symbol_file", "must end with .map.txt")
@@ -264,9 +450,15 @@
 	}
 
 	symbolFile := String(c.properties.Symbol_file)
-	objs, versionScript := compileStubLibrary(ctx, flags, symbolFile,
-		c.apiLevel.String(), "")
-	c.versionScriptPath = versionScript
+	nativeAbiResult := parseNativeAbiDefinition(ctx, symbolFile, c.apiLevel, "")
+	objs := compileStubLibrary(ctx, flags, nativeAbiResult.stubSrc)
+	c.versionScriptPath = nativeAbiResult.versionScript
+	if canDumpAbi() {
+		c.dumpAbi(ctx, nativeAbiResult.symbolList)
+		if canDiffAbi() {
+			c.diffAbi(ctx)
+		}
+	}
 	if c.apiLevel.IsCurrent() && ctx.PrimaryArch() {
 		c.parsedCoverageXmlPath = parseSymbolFileForCoverage(ctx, symbolFile)
 	}
diff --git a/cc/ndk_sysroot.go b/cc/ndk_sysroot.go
index d8c500e..4a9601b 100644
--- a/cc/ndk_sysroot.go
+++ b/cc/ndk_sysroot.go
@@ -144,10 +144,9 @@
 		Inputs:      licensePaths,
 	})
 
-	baseDepPaths := append(installPaths, combinedLicense)
+	baseDepPaths := append(installPaths, combinedLicense,
+		getNdkAbiDiffTimestampFile(ctx))
 
-	// There's a dummy "ndk" rule defined in ndk/Android.mk that depends on
-	// this. `m ndk` will build the sysroots.
 	ctx.Build(pctx, android.BuildParams{
 		Rule:      android.Touch,
 		Output:    getNdkBaseTimestampFile(ctx),
@@ -156,6 +155,11 @@
 
 	fullDepPaths := append(staticLibInstallPaths, getNdkBaseTimestampFile(ctx))
 
+	// There's a phony "ndk" rule defined in core/main.mk that depends on this.
+	// `m ndk` will build the sysroots for the architectures in the current
+	// lunch target. `build/soong/scripts/build-ndk-prebuilts.sh` will build the
+	// sysroots for all the NDK architectures and package them so they can be
+	// imported into the NDK's build.
 	ctx.Build(pctx, android.BuildParams{
 		Rule:      android.Touch,
 		Output:    getNdkFullTimestampFile(ctx),
diff --git a/cc/ndkstubgen/__init__.py b/cc/ndkstubgen/__init__.py
index 86bf6ff..2387e69 100755
--- a/cc/ndkstubgen/__init__.py
+++ b/cc/ndkstubgen/__init__.py
@@ -18,7 +18,7 @@
 import argparse
 import json
 import logging
-import os
+from pathlib import Path
 import sys
 from typing import Iterable, TextIO
 
@@ -28,10 +28,12 @@
 
 class Generator:
     """Output generator that writes stub source files and version scripts."""
-    def __init__(self, src_file: TextIO, version_script: TextIO, arch: Arch,
-                 api: int, llndk: bool, apex: bool) -> None:
+    def __init__(self, src_file: TextIO, version_script: TextIO,
+                 symbol_list: TextIO, arch: Arch, api: int, llndk: bool,
+                 apex: bool) -> None:
         self.src_file = src_file
         self.version_script = version_script
+        self.symbol_list = symbol_list
         self.arch = arch
         self.api = api
         self.llndk = llndk
@@ -39,6 +41,7 @@
 
     def write(self, versions: Iterable[Version]) -> None:
         """Writes all symbol data to the output files."""
+        self.symbol_list.write('[abi_symbol_list]\n')
         for version in versions:
             self.write_version(version)
 
@@ -76,11 +79,11 @@
                     weak = '__attribute__((weak)) '
 
                 if 'var' in symbol.tags:
-                    self.src_file.write('{}int {} = 0;\n'.format(
-                        weak, symbol.name))
+                    self.src_file.write(f'{weak}int {symbol.name} = 0;\n')
                 else:
-                    self.src_file.write('{}void {}() {{}}\n'.format(
-                        weak, symbol.name))
+                    self.src_file.write(f'{weak}void {symbol.name}() {{}}\n')
+
+                self.symbol_list.write(f'{symbol.name}\n')
 
             if not version_empty and section_versioned:
                 base = '' if version.base is None else ' ' + version.base
@@ -91,6 +94,10 @@
     """Parses and returns command line arguments."""
     parser = argparse.ArgumentParser()
 
+    def resolved_path(raw: str) -> Path:
+        """Returns a resolved Path for the given string."""
+        return Path(raw).resolve()
+
     parser.add_argument('-v', '--verbose', action='count', default=0)
 
     parser.add_argument(
@@ -103,26 +110,23 @@
     parser.add_argument(
         '--apex', action='store_true', help='Use the APEX variant.')
 
-    # https://github.com/python/mypy/issues/1317
-    # mypy has issues with using os.path.realpath as an argument here.
-    parser.add_argument(
-        '--api-map',
-        type=os.path.realpath,  # type: ignore
-        required=True,
-        help='Path to the API level map JSON file.')
+    parser.add_argument('--api-map',
+                        type=resolved_path,
+                        required=True,
+                        help='Path to the API level map JSON file.')
 
-    parser.add_argument(
-        'symbol_file',
-        type=os.path.realpath,  # type: ignore
-        help='Path to symbol file.')
-    parser.add_argument(
-        'stub_src',
-        type=os.path.realpath,  # type: ignore
-        help='Path to output stub source file.')
-    parser.add_argument(
-        'version_script',
-        type=os.path.realpath,  # type: ignore
-        help='Path to output version script.')
+    parser.add_argument('symbol_file',
+                        type=resolved_path,
+                        help='Path to symbol file.')
+    parser.add_argument('stub_src',
+                        type=resolved_path,
+                        help='Path to output stub source file.')
+    parser.add_argument('version_script',
+                        type=resolved_path,
+                        help='Path to output version script.')
+    parser.add_argument('symbol_list',
+                        type=resolved_path,
+                        help='Path to output abigail symbol list.')
 
     return parser.parse_args()
 
@@ -131,7 +135,7 @@
     """Program entry point."""
     args = parse_args()
 
-    with open(args.api_map) as map_file:
+    with args.api_map.open() as map_file:
         api_map = json.load(map_file)
     api = symbolfile.decode_api_level(args.api, api_map)
 
@@ -141,19 +145,20 @@
         verbosity = 2
     logging.basicConfig(level=verbose_map[verbosity])
 
-    with open(args.symbol_file) as symbol_file:
+    with args.symbol_file.open() as symbol_file:
         try:
             versions = symbolfile.SymbolFileParser(symbol_file, api_map,
                                                    args.arch, api, args.llndk,
                                                    args.apex).parse()
         except symbolfile.MultiplyDefinedSymbolError as ex:
-            sys.exit('{}: error: {}'.format(args.symbol_file, ex))
+            sys.exit(f'{args.symbol_file}: error: {ex}')
 
-    with open(args.stub_src, 'w') as src_file:
-        with open(args.version_script, 'w') as version_file:
-            generator = Generator(src_file, version_file, args.arch, api,
-                                  args.llndk, args.apex)
-            generator.write(versions)
+    with args.stub_src.open('w') as src_file:
+        with args.version_script.open('w') as version_script:
+            with args.symbol_list.open('w') as symbol_list:
+                generator = Generator(src_file, version_script, symbol_list,
+                                      args.arch, api, args.llndk, args.apex)
+                generator.write(versions)
 
 
 if __name__ == '__main__':
diff --git a/cc/ndkstubgen/test_ndkstubgen.py b/cc/ndkstubgen/test_ndkstubgen.py
index 6d2c9d6..3dbab61 100755
--- a/cc/ndkstubgen/test_ndkstubgen.py
+++ b/cc/ndkstubgen/test_ndkstubgen.py
@@ -33,8 +33,10 @@
         # OmitVersionTest, PrivateVersionTest, and SymbolPresenceTest.
         src_file = io.StringIO()
         version_file = io.StringIO()
-        generator = ndkstubgen.Generator(src_file, version_file, Arch('arm'),
-                                         9, False, False)
+        symbol_list_file = io.StringIO()
+        generator = ndkstubgen.Generator(src_file,
+                                         version_file, symbol_list_file,
+                                         Arch('arm'), 9, False, False)
 
         version = symbolfile.Version('VERSION_PRIVATE', None, [], [
             symbolfile.Symbol('foo', []),
@@ -62,8 +64,10 @@
         # SymbolPresenceTest.
         src_file = io.StringIO()
         version_file = io.StringIO()
-        generator = ndkstubgen.Generator(src_file, version_file, Arch('arm'),
-                                         9, False, False)
+        symbol_list_file = io.StringIO()
+        generator = ndkstubgen.Generator(src_file,
+                                         version_file, symbol_list_file,
+                                         Arch('arm'), 9, False, False)
 
         version = symbolfile.Version('VERSION_1', None, [], [
             symbolfile.Symbol('foo', [Tag('x86')]),
@@ -96,8 +100,10 @@
     def test_write(self) -> None:
         src_file = io.StringIO()
         version_file = io.StringIO()
-        generator = ndkstubgen.Generator(src_file, version_file, Arch('arm'),
-                                         9, False, False)
+        symbol_list_file = io.StringIO()
+        generator = ndkstubgen.Generator(src_file,
+                                         version_file, symbol_list_file,
+                                         Arch('arm'), 9, False, False)
 
         versions = [
             symbolfile.Version('VERSION_1', None, [], [
@@ -141,6 +147,17 @@
         """)
         self.assertEqual(expected_version, version_file.getvalue())
 
+        expected_allowlist = textwrap.dedent("""\
+            [abi_symbol_list]
+            foo
+            bar
+            woodly
+            doodly
+            baz
+            qux
+        """)
+        self.assertEqual(expected_allowlist, symbol_list_file.getvalue())
+
 
 class IntegrationTest(unittest.TestCase):
     def test_integration(self) -> None:
@@ -186,8 +203,10 @@
 
         src_file = io.StringIO()
         version_file = io.StringIO()
-        generator = ndkstubgen.Generator(src_file, version_file, Arch('arm'),
-                                         9, False, False)
+        symbol_list_file = io.StringIO()
+        generator = ndkstubgen.Generator(src_file,
+                                         version_file, symbol_list_file,
+                                         Arch('arm'), 9, False, False)
         generator.write(versions)
 
         expected_src = textwrap.dedent("""\
@@ -215,6 +234,16 @@
         """)
         self.assertEqual(expected_version, version_file.getvalue())
 
+        expected_allowlist = textwrap.dedent("""\
+            [abi_symbol_list]
+            foo
+            baz
+            qux
+            wibble
+            wobble
+        """)
+        self.assertEqual(expected_allowlist, symbol_list_file.getvalue())
+
     def test_integration_future_api(self) -> None:
         api_map = {
             'O': 9000,
@@ -238,8 +267,10 @@
 
         src_file = io.StringIO()
         version_file = io.StringIO()
-        generator = ndkstubgen.Generator(src_file, version_file, Arch('arm'),
-                                         9001, False, False)
+        symbol_list_file = io.StringIO()
+        generator = ndkstubgen.Generator(src_file,
+                                         version_file, symbol_list_file,
+                                         Arch('arm'), 9001, False, False)
         generator.write(versions)
 
         expected_src = textwrap.dedent("""\
@@ -257,6 +288,13 @@
         """)
         self.assertEqual(expected_version, version_file.getvalue())
 
+        expected_allowlist = textwrap.dedent("""\
+            [abi_symbol_list]
+            foo
+            bar
+        """)
+        self.assertEqual(expected_allowlist, symbol_list_file.getvalue())
+
     def test_multiple_definition(self) -> None:
         input_file = io.StringIO(textwrap.dedent("""\
             VERSION_1 {
@@ -336,8 +374,10 @@
 
         src_file = io.StringIO()
         version_file = io.StringIO()
-        generator = ndkstubgen.Generator(src_file, version_file, Arch('arm'),
-                                         9, False, True)
+        symbol_list_file = io.StringIO()
+        generator = ndkstubgen.Generator(src_file,
+                                         version_file, symbol_list_file,
+                                         Arch('arm'), 9, False, True)
         generator.write(versions)
 
         expected_src = textwrap.dedent("""\
diff --git a/cc/sanitize.go b/cc/sanitize.go
index f486ee4..513730a 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -854,7 +854,7 @@
 // as vendor snapshot. Such modules must create both cfi and non-cfi variants,
 // except for ones which explicitly disable cfi.
 func needsCfiForVendorSnapshot(mctx android.TopDownMutatorContext) bool {
-	if isVendorProprietaryModule(mctx) {
+	if IsVendorProprietaryModule(mctx) {
 		return false
 	}
 
@@ -1192,7 +1192,7 @@
 				if c.Device() {
 					variations = append(variations, c.ImageVariation())
 				}
-				c.addSharedLibDependenciesWithVersions(mctx, variations, depTag, runtimeLibrary, "", true)
+				AddSharedLibDependenciesWithVersions(mctx, c, variations, depTag, runtimeLibrary, "", true)
 			}
 			// static lib does not have dependency to the runtime library. The
 			// dependency will be added to the executables or shared libs using
diff --git a/cc/snapshot_prebuilt.go b/cc/snapshot_prebuilt.go
index bf11b11..81590a5 100644
--- a/cc/snapshot_prebuilt.go
+++ b/cc/snapshot_prebuilt.go
@@ -28,7 +28,7 @@
 
 // Defines the specifics of different images to which the snapshot process is applicable, e.g.,
 // vendor, recovery, ramdisk.
-type snapshotImage interface {
+type SnapshotImage interface {
 	// Returns true if a snapshot should be generated for this image.
 	shouldGenerateSnapshot(ctx android.SingletonContext) bool
 
@@ -108,7 +108,7 @@
 	}
 )
 
-func (vendorSnapshotImage) init(ctx android.RegistrationContext) {
+func (vendorSnapshotImage) Init(ctx android.RegistrationContext) {
 	ctx.RegisterSingletonType("vendor-snapshot", VendorSnapshotSingleton)
 	ctx.RegisterModuleType("vendor_snapshot", vendorSnapshotFactory)
 	ctx.RegisterModuleType("vendor_snapshot_shared", VendorSnapshotSharedFactory)
@@ -120,6 +120,10 @@
 	ctx.RegisterSingletonType("vendor-fake-snapshot", VendorFakeSnapshotSingleton)
 }
 
+func (vendorSnapshotImage) RegisterAdditionalModule(ctx android.RegistrationContext, name string, factory android.ModuleFactory) {
+	ctx.RegisterModuleType(name, factory)
+}
+
 func (vendorSnapshotImage) shouldGenerateSnapshot(ctx android.SingletonContext) bool {
 	// BOARD_VNDK_VERSION must be set to 'current' in order to generate a snapshot.
 	return ctx.DeviceConfig().VndkVersion() == "current"
@@ -254,26 +258,28 @@
 	return recoverySuffix
 }
 
-var vendorSnapshotImageSingleton vendorSnapshotImage
+var VendorSnapshotImageSingleton vendorSnapshotImage
 var recoverySnapshotImageSingleton recoverySnapshotImage
 
 func init() {
-	vendorSnapshotImageSingleton.init(android.InitRegistrationContext)
+	VendorSnapshotImageSingleton.Init(android.InitRegistrationContext)
 	recoverySnapshotImageSingleton.init(android.InitRegistrationContext)
 }
 
 const (
 	snapshotHeaderSuffix = "_header."
-	snapshotSharedSuffix = "_shared."
-	snapshotStaticSuffix = "_static."
+	SnapshotSharedSuffix = "_shared."
+	SnapshotStaticSuffix = "_static."
 	snapshotBinarySuffix = "_binary."
 	snapshotObjectSuffix = "_object."
+	SnapshotRlibSuffix   = "_rlib."
 )
 
 type SnapshotProperties struct {
 	Header_libs []string `android:"arch_variant"`
 	Static_libs []string `android:"arch_variant"`
 	Shared_libs []string `android:"arch_variant"`
+	Rlibs       []string `android:"arch_variant"`
 	Vndk_libs   []string `android:"arch_variant"`
 	Binaries    []string `android:"arch_variant"`
 	Objects     []string `android:"arch_variant"`
@@ -284,14 +290,14 @@
 
 	properties SnapshotProperties
 
-	baseSnapshot baseSnapshotDecorator
+	baseSnapshot BaseSnapshotDecorator
 
-	image snapshotImage
+	image SnapshotImage
 }
 
 func (s *snapshot) ImageMutatorBegin(ctx android.BaseModuleContext) {
 	cfg := ctx.DeviceConfig()
-	if !s.image.isUsingSnapshot(cfg) || s.image.targetSnapshotVersion(cfg) != s.baseSnapshot.version() {
+	if !s.image.isUsingSnapshot(cfg) || s.image.targetSnapshotVersion(cfg) != s.baseSnapshot.Version() {
 		s.Disable()
 	}
 }
@@ -341,7 +347,7 @@
 		for _, name := range names {
 			snapshotMap[name] = name +
 				getSnapshotNameSuffix(snapshotSuffix+moduleSuffix,
-					s.baseSnapshot.version(),
+					s.baseSnapshot.Version(),
 					ctx.DeviceConfig().Arches()[0].ArchType.String())
 		}
 		return snapshotMap
@@ -351,8 +357,9 @@
 	headers := collectSnapshotMap(s.properties.Header_libs, snapshotSuffix, snapshotHeaderSuffix)
 	binaries := collectSnapshotMap(s.properties.Binaries, snapshotSuffix, snapshotBinarySuffix)
 	objects := collectSnapshotMap(s.properties.Objects, snapshotSuffix, snapshotObjectSuffix)
-	staticLibs := collectSnapshotMap(s.properties.Static_libs, snapshotSuffix, snapshotStaticSuffix)
-	sharedLibs := collectSnapshotMap(s.properties.Shared_libs, snapshotSuffix, snapshotSharedSuffix)
+	staticLibs := collectSnapshotMap(s.properties.Static_libs, snapshotSuffix, SnapshotStaticSuffix)
+	sharedLibs := collectSnapshotMap(s.properties.Shared_libs, snapshotSuffix, SnapshotSharedSuffix)
+	rlibs := collectSnapshotMap(s.properties.Rlibs, snapshotSuffix, SnapshotRlibSuffix)
 	vndkLibs := collectSnapshotMap(s.properties.Vndk_libs, "", vndkSuffix)
 	for k, v := range vndkLibs {
 		sharedLibs[k] = v
@@ -364,11 +371,12 @@
 		Objects:    objects,
 		StaticLibs: staticLibs,
 		SharedLibs: sharedLibs,
+		Rlibs:      rlibs,
 	})
 }
 
 type SnapshotInfo struct {
-	HeaderLibs, Binaries, Objects, StaticLibs, SharedLibs map[string]string
+	HeaderLibs, Binaries, Objects, StaticLibs, SharedLibs, Rlibs map[string]string
 }
 
 var SnapshotInfoProvider = blueprint.NewMutatorProvider(SnapshotInfo{}, "deps")
@@ -376,14 +384,14 @@
 var _ android.ImageInterface = (*snapshot)(nil)
 
 func vendorSnapshotFactory() android.Module {
-	return snapshotFactory(vendorSnapshotImageSingleton)
+	return snapshotFactory(VendorSnapshotImageSingleton)
 }
 
 func recoverySnapshotFactory() android.Module {
 	return snapshotFactory(recoverySnapshotImageSingleton)
 }
 
-func snapshotFactory(image snapshotImage) android.Module {
+func snapshotFactory(image SnapshotImage) android.Module {
 	snapshot := &snapshot{}
 	snapshot.image = image
 	snapshot.AddProperties(
@@ -393,7 +401,7 @@
 	return snapshot
 }
 
-type baseSnapshotDecoratorProperties struct {
+type BaseSnapshotDecoratorProperties struct {
 	// snapshot version.
 	Version string
 
@@ -408,7 +416,7 @@
 	ModuleSuffix string `blueprint:"mutated"`
 }
 
-// baseSnapshotDecorator provides common basic functions for all snapshot modules, such as snapshot
+// BaseSnapshotDecorator provides common basic functions for all snapshot modules, such as snapshot
 // version, snapshot arch, etc. It also adds a special suffix to Soong module name, so it doesn't
 // collide with source modules. e.g. the following example module,
 //
@@ -420,40 +428,40 @@
 // }
 //
 // will be seen as "libbase.vendor_static.30.arm64" by Soong.
-type baseSnapshotDecorator struct {
-	baseProperties baseSnapshotDecoratorProperties
-	image          snapshotImage
+type BaseSnapshotDecorator struct {
+	baseProperties BaseSnapshotDecoratorProperties
+	image          SnapshotImage
 }
 
-func (p *baseSnapshotDecorator) Name(name string) string {
+func (p *BaseSnapshotDecorator) Name(name string) string {
 	return name + p.NameSuffix()
 }
 
-func (p *baseSnapshotDecorator) NameSuffix() string {
-	return getSnapshotNameSuffix(p.moduleSuffix(), p.version(), p.arch())
+func (p *BaseSnapshotDecorator) NameSuffix() string {
+	return getSnapshotNameSuffix(p.moduleSuffix(), p.Version(), p.Arch())
 }
 
-func (p *baseSnapshotDecorator) version() string {
+func (p *BaseSnapshotDecorator) Version() string {
 	return p.baseProperties.Version
 }
 
-func (p *baseSnapshotDecorator) arch() string {
+func (p *BaseSnapshotDecorator) Arch() string {
 	return p.baseProperties.Target_arch
 }
 
-func (p *baseSnapshotDecorator) moduleSuffix() string {
+func (p *BaseSnapshotDecorator) moduleSuffix() string {
 	return p.baseProperties.ModuleSuffix
 }
 
-func (p *baseSnapshotDecorator) isSnapshotPrebuilt() bool {
+func (p *BaseSnapshotDecorator) IsSnapshotPrebuilt() bool {
 	return true
 }
 
-func (p *baseSnapshotDecorator) snapshotAndroidMkSuffix() string {
+func (p *BaseSnapshotDecorator) SnapshotAndroidMkSuffix() string {
 	return p.baseProperties.Androidmk_suffix
 }
 
-func (p *baseSnapshotDecorator) setSnapshotAndroidMkSuffix(ctx android.ModuleContext, variant string) {
+func (p *BaseSnapshotDecorator) SetSnapshotAndroidMkSuffix(ctx android.ModuleContext, variant string) {
 	// If there are any 2 or more variations among {core, product, vendor, recovery}
 	// we have to add the androidmk suffix to avoid duplicate modules with the same
 	// name.
@@ -461,7 +469,7 @@
 		Mutator:   "image",
 		Variation: android.CoreVariation})
 
-	if ctx.OtherModuleFarDependencyVariantExists(variations, ctx.Module().(*Module).BaseModuleName()) {
+	if ctx.OtherModuleFarDependencyVariantExists(variations, ctx.Module().(LinkableInterface).BaseModuleName()) {
 		p.baseProperties.Androidmk_suffix = p.image.moduleNameSuffix()
 		return
 	}
@@ -470,12 +478,12 @@
 		Mutator:   "image",
 		Variation: ProductVariationPrefix + ctx.DeviceConfig().PlatformVndkVersion()})
 
-	if ctx.OtherModuleFarDependencyVariantExists(variations, ctx.Module().(*Module).BaseModuleName()) {
+	if ctx.OtherModuleFarDependencyVariantExists(variations, ctx.Module().(LinkableInterface).BaseModuleName()) {
 		p.baseProperties.Androidmk_suffix = p.image.moduleNameSuffix()
 		return
 	}
 
-	images := []snapshotImage{vendorSnapshotImageSingleton, recoverySnapshotImageSingleton}
+	images := []SnapshotImage{VendorSnapshotImageSingleton, recoverySnapshotImageSingleton}
 
 	for _, image := range images {
 		if p.image == image {
@@ -486,10 +494,10 @@
 			Variation: image.imageVariantName(ctx.DeviceConfig())})
 
 		if ctx.OtherModuleFarDependencyVariantExists(variations,
-			ctx.Module().(*Module).BaseModuleName()+
+			ctx.Module().(LinkableInterface).BaseModuleName()+
 				getSnapshotNameSuffix(
 					image.moduleNameSuffix()+variant,
-					p.version(),
+					p.Version(),
 					ctx.DeviceConfig().Arches()[0].ArchType.String())) {
 			p.baseProperties.Androidmk_suffix = p.image.moduleNameSuffix()
 			return
@@ -501,7 +509,7 @@
 
 // Call this with a module suffix after creating a snapshot module, such as
 // vendorSnapshotSharedSuffix, recoverySnapshotBinarySuffix, etc.
-func (p *baseSnapshotDecorator) init(m *Module, image snapshotImage, moduleSuffix string) {
+func (p *BaseSnapshotDecorator) Init(m LinkableInterface, image SnapshotImage, moduleSuffix string) {
 	p.image = image
 	p.baseProperties.ModuleSuffix = image.moduleNameSuffix() + moduleSuffix
 	m.AddProperties(&p.baseProperties)
@@ -512,8 +520,8 @@
 
 // vendorSnapshotLoadHook disables snapshots if it's not BOARD_VNDK_VERSION.
 // As vendor snapshot is only for vendor, such modules won't be used at all.
-func vendorSnapshotLoadHook(ctx android.LoadHookContext, p *baseSnapshotDecorator) {
-	if p.version() != ctx.DeviceConfig().VndkVersion() {
+func vendorSnapshotLoadHook(ctx android.LoadHookContext, p *BaseSnapshotDecorator) {
+	if p.Version() != ctx.DeviceConfig().VndkVersion() {
 		ctx.Module().Disable()
 		return
 	}
@@ -528,7 +536,7 @@
 // include directories, c flags, sanitize dependency information, etc.
 //
 // These modules are auto-generated by development/vendor_snapshot/update.py.
-type snapshotLibraryProperties struct {
+type SnapshotLibraryProperties struct {
 	// Prebuilt file for each arch.
 	Src *string `android:"arch_variant"`
 
@@ -554,14 +562,14 @@
 }
 
 type snapshotLibraryDecorator struct {
-	baseSnapshotDecorator
+	BaseSnapshotDecorator
 	*libraryDecorator
-	properties          snapshotLibraryProperties
+	properties          SnapshotLibraryProperties
 	sanitizerProperties struct {
 		CfiEnabled bool `blueprint:"mutated"`
 
 		// Library flags for cfi variant.
-		Cfi snapshotLibraryProperties `android:"arch_variant"`
+		Cfi SnapshotLibraryProperties `android:"arch_variant"`
 	}
 }
 
@@ -570,9 +578,9 @@
 	return p.libraryDecorator.linkerFlags(ctx, flags)
 }
 
-func (p *snapshotLibraryDecorator) matchesWithDevice(config android.DeviceConfig) bool {
+func (p *snapshotLibraryDecorator) MatchesWithDevice(config android.DeviceConfig) bool {
 	arches := config.Arches()
-	if len(arches) == 0 || arches[0].ArchType.String() != p.arch() {
+	if len(arches) == 0 || arches[0].ArchType.String() != p.Arch() {
 		return false
 	}
 	if !p.header() && p.properties.Src == nil {
@@ -587,14 +595,14 @@
 func (p *snapshotLibraryDecorator) link(ctx ModuleContext, flags Flags, deps PathDeps, objs Objects) android.Path {
 	var variant string
 	if p.shared() {
-		variant = snapshotSharedSuffix
+		variant = SnapshotSharedSuffix
 	} else if p.static() {
-		variant = snapshotStaticSuffix
+		variant = SnapshotStaticSuffix
 	} else {
 		variant = snapshotHeaderSuffix
 	}
 
-	p.setSnapshotAndroidMkSuffix(ctx, variant)
+	p.SetSnapshotAndroidMkSuffix(ctx, variant)
 
 	if p.header() {
 		return p.libraryDecorator.link(ctx, flags, deps, objs)
@@ -604,7 +612,7 @@
 		p.properties = p.sanitizerProperties.Cfi
 	}
 
-	if !p.matchesWithDevice(ctx.DeviceConfig()) {
+	if !p.MatchesWithDevice(ctx.DeviceConfig()) {
 		return nil
 	}
 
@@ -657,7 +665,7 @@
 }
 
 func (p *snapshotLibraryDecorator) install(ctx ModuleContext, file android.Path) {
-	if p.matchesWithDevice(ctx.DeviceConfig()) && (p.shared() || p.static()) {
+	if p.MatchesWithDevice(ctx.DeviceConfig()) && (p.shared() || p.static()) {
 		p.baseInstaller.install(ctx, file)
 	}
 }
@@ -687,7 +695,7 @@
 	}
 }
 
-func snapshotLibraryFactory(image snapshotImage, moduleSuffix string) (*Module, *snapshotLibraryDecorator) {
+func snapshotLibraryFactory(image SnapshotImage, moduleSuffix string) (*Module, *snapshotLibraryDecorator) {
 	module, library := NewLibrary(android.DeviceSupported)
 
 	module.stl = nil
@@ -710,7 +718,7 @@
 	module.linker = prebuilt
 	module.installer = prebuilt
 
-	prebuilt.init(module, image, moduleSuffix)
+	prebuilt.Init(module, image, moduleSuffix)
 	module.AddProperties(
 		&prebuilt.properties,
 		&prebuilt.sanitizerProperties,
@@ -724,7 +732,7 @@
 // overrides the vendor variant of the cc shared library with the same name, if BOARD_VNDK_VERSION
 // is set.
 func VendorSnapshotSharedFactory() android.Module {
-	module, prebuilt := snapshotLibraryFactory(vendorSnapshotImageSingleton, snapshotSharedSuffix)
+	module, prebuilt := snapshotLibraryFactory(VendorSnapshotImageSingleton, SnapshotSharedSuffix)
 	prebuilt.libraryDecorator.BuildOnlyShared()
 	return module.Init()
 }
@@ -734,7 +742,7 @@
 // overrides the recovery variant of the cc shared library with the same name, if BOARD_VNDK_VERSION
 // is set.
 func RecoverySnapshotSharedFactory() android.Module {
-	module, prebuilt := snapshotLibraryFactory(recoverySnapshotImageSingleton, snapshotSharedSuffix)
+	module, prebuilt := snapshotLibraryFactory(recoverySnapshotImageSingleton, SnapshotSharedSuffix)
 	prebuilt.libraryDecorator.BuildOnlyShared()
 	return module.Init()
 }
@@ -744,7 +752,7 @@
 // overrides the vendor variant of the cc static library with the same name, if BOARD_VNDK_VERSION
 // is set.
 func VendorSnapshotStaticFactory() android.Module {
-	module, prebuilt := snapshotLibraryFactory(vendorSnapshotImageSingleton, snapshotStaticSuffix)
+	module, prebuilt := snapshotLibraryFactory(VendorSnapshotImageSingleton, SnapshotStaticSuffix)
 	prebuilt.libraryDecorator.BuildOnlyStatic()
 	return module.Init()
 }
@@ -754,7 +762,7 @@
 // overrides the recovery variant of the cc static library with the same name, if BOARD_VNDK_VERSION
 // is set.
 func RecoverySnapshotStaticFactory() android.Module {
-	module, prebuilt := snapshotLibraryFactory(recoverySnapshotImageSingleton, snapshotStaticSuffix)
+	module, prebuilt := snapshotLibraryFactory(recoverySnapshotImageSingleton, SnapshotStaticSuffix)
 	prebuilt.libraryDecorator.BuildOnlyStatic()
 	return module.Init()
 }
@@ -764,7 +772,7 @@
 // overrides the vendor variant of the cc header library with the same name, if BOARD_VNDK_VERSION
 // is set.
 func VendorSnapshotHeaderFactory() android.Module {
-	module, prebuilt := snapshotLibraryFactory(vendorSnapshotImageSingleton, snapshotHeaderSuffix)
+	module, prebuilt := snapshotLibraryFactory(VendorSnapshotImageSingleton, snapshotHeaderSuffix)
 	prebuilt.libraryDecorator.HeaderOnly()
 	return module.Init()
 }
@@ -794,13 +802,13 @@
 }
 
 type snapshotBinaryDecorator struct {
-	baseSnapshotDecorator
+	BaseSnapshotDecorator
 	*binaryDecorator
 	properties snapshotBinaryProperties
 }
 
-func (p *snapshotBinaryDecorator) matchesWithDevice(config android.DeviceConfig) bool {
-	if config.DeviceArch() != p.arch() {
+func (p *snapshotBinaryDecorator) MatchesWithDevice(config android.DeviceConfig) bool {
+	if config.DeviceArch() != p.Arch() {
 		return false
 	}
 	if p.properties.Src == nil {
@@ -812,9 +820,9 @@
 // cc modules' link functions are to link compiled objects into final binaries.
 // As snapshots are prebuilts, this just returns the prebuilt binary
 func (p *snapshotBinaryDecorator) link(ctx ModuleContext, flags Flags, deps PathDeps, objs Objects) android.Path {
-	p.setSnapshotAndroidMkSuffix(ctx, snapshotBinarySuffix)
+	p.SetSnapshotAndroidMkSuffix(ctx, snapshotBinarySuffix)
 
-	if !p.matchesWithDevice(ctx.DeviceConfig()) {
+	if !p.MatchesWithDevice(ctx.DeviceConfig()) {
 		return nil
 	}
 
@@ -842,7 +850,7 @@
 // development/vendor_snapshot/update.py. As a part of vendor snapshot, vendor_snapshot_binary
 // overrides the vendor variant of the cc binary with the same name, if BOARD_VNDK_VERSION is set.
 func VendorSnapshotBinaryFactory() android.Module {
-	return snapshotBinaryFactory(vendorSnapshotImageSingleton, snapshotBinarySuffix)
+	return snapshotBinaryFactory(VendorSnapshotImageSingleton, snapshotBinarySuffix)
 }
 
 // recovery_snapshot_binary is a special prebuilt executable binary which is auto-generated by
@@ -852,7 +860,7 @@
 	return snapshotBinaryFactory(recoverySnapshotImageSingleton, snapshotBinarySuffix)
 }
 
-func snapshotBinaryFactory(image snapshotImage, moduleSuffix string) android.Module {
+func snapshotBinaryFactory(image SnapshotImage, moduleSuffix string) android.Module {
 	module, binary := NewBinary(android.DeviceSupported)
 	binary.baseLinker.Properties.No_libcrt = BoolPtr(true)
 	binary.baseLinker.Properties.Nocrt = BoolPtr(true)
@@ -871,7 +879,7 @@
 	module.stl = nil
 	module.linker = prebuilt
 
-	prebuilt.init(module, image, moduleSuffix)
+	prebuilt.Init(module, image, moduleSuffix)
 	module.AddProperties(&prebuilt.properties)
 	return module.Init()
 }
@@ -889,13 +897,13 @@
 }
 
 type snapshotObjectLinker struct {
-	baseSnapshotDecorator
+	BaseSnapshotDecorator
 	objectLinker
 	properties vendorSnapshotObjectProperties
 }
 
-func (p *snapshotObjectLinker) matchesWithDevice(config android.DeviceConfig) bool {
-	if config.DeviceArch() != p.arch() {
+func (p *snapshotObjectLinker) MatchesWithDevice(config android.DeviceConfig) bool {
+	if config.DeviceArch() != p.Arch() {
 		return false
 	}
 	if p.properties.Src == nil {
@@ -907,9 +915,9 @@
 // cc modules' link functions are to link compiled objects into final binaries.
 // As snapshots are prebuilts, this just returns the prebuilt binary
 func (p *snapshotObjectLinker) link(ctx ModuleContext, flags Flags, deps PathDeps, objs Objects) android.Path {
-	p.setSnapshotAndroidMkSuffix(ctx, snapshotObjectSuffix)
+	p.SetSnapshotAndroidMkSuffix(ctx, snapshotObjectSuffix)
 
-	if !p.matchesWithDevice(ctx.DeviceConfig()) {
+	if !p.MatchesWithDevice(ctx.DeviceConfig()) {
 		return nil
 	}
 
@@ -933,7 +941,7 @@
 	}
 	module.linker = prebuilt
 
-	prebuilt.init(module, vendorSnapshotImageSingleton, snapshotObjectSuffix)
+	prebuilt.Init(module, VendorSnapshotImageSingleton, snapshotObjectSuffix)
 	module.AddProperties(&prebuilt.properties)
 	return module.Init()
 }
@@ -951,19 +959,19 @@
 	}
 	module.linker = prebuilt
 
-	prebuilt.init(module, recoverySnapshotImageSingleton, snapshotObjectSuffix)
+	prebuilt.Init(module, recoverySnapshotImageSingleton, snapshotObjectSuffix)
 	module.AddProperties(&prebuilt.properties)
 	return module.Init()
 }
 
-type snapshotInterface interface {
-	matchesWithDevice(config android.DeviceConfig) bool
-	isSnapshotPrebuilt() bool
-	version() string
-	snapshotAndroidMkSuffix() string
+type SnapshotInterface interface {
+	MatchesWithDevice(config android.DeviceConfig) bool
+	IsSnapshotPrebuilt() bool
+	Version() string
+	SnapshotAndroidMkSuffix() string
 }
 
-var _ snapshotInterface = (*vndkPrebuiltLibraryDecorator)(nil)
-var _ snapshotInterface = (*snapshotLibraryDecorator)(nil)
-var _ snapshotInterface = (*snapshotBinaryDecorator)(nil)
-var _ snapshotInterface = (*snapshotObjectLinker)(nil)
+var _ SnapshotInterface = (*vndkPrebuiltLibraryDecorator)(nil)
+var _ SnapshotInterface = (*snapshotLibraryDecorator)(nil)
+var _ SnapshotInterface = (*snapshotBinaryDecorator)(nil)
+var _ SnapshotInterface = (*snapshotObjectLinker)(nil)
diff --git a/cc/snapshot_utils.go b/cc/snapshot_utils.go
index 8eb6164..a6c8ed5 100644
--- a/cc/snapshot_utils.go
+++ b/cc/snapshot_utils.go
@@ -20,7 +20,7 @@
 )
 
 var (
-	headerExts = []string{".h", ".hh", ".hpp", ".hxx", ".h++", ".inl", ".inc", ".ipp", ".h.generic"}
+	HeaderExts = []string{".h", ".hh", ".hpp", ".hxx", ".h++", ".inl", ".inc", ".ipp", ".h.generic"}
 )
 
 func (m *Module) IsSnapshotLibrary() bool {
@@ -109,7 +109,7 @@
 		return ctx.Config().VndkSnapshotBuildArtifacts()
 	}
 
-	for _, image := range []snapshotImage{vendorSnapshotImageSingleton, recoverySnapshotImageSingleton} {
+	for _, image := range []SnapshotImage{VendorSnapshotImageSingleton, recoverySnapshotImageSingleton} {
 		if isSnapshotAware(ctx.DeviceConfig(), m, image.isProprietaryPath(ctx.ModuleDir(), ctx.DeviceConfig()), apexInfo, image) {
 			return true
 		}
diff --git a/cc/testing.go b/cc/testing.go
index 15f7ebb..f5c5ec5 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -15,6 +15,9 @@
 package cc
 
 import (
+	"path/filepath"
+	"testing"
+
 	"android/soong/android"
 	"android/soong/genrule"
 )
@@ -625,7 +628,7 @@
 var PrepareForTestWithCcIncludeVndk = android.GroupFixturePreparers(
 	PrepareForIntegrationTestWithCc,
 	android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
-		vendorSnapshotImageSingleton.init(ctx)
+		VendorSnapshotImageSingleton.Init(ctx)
 		recoverySnapshotImageSingleton.init(ctx)
 		ctx.RegisterSingletonType("vndk-snapshot", VndkSnapshotSingleton)
 	}),
@@ -674,7 +677,7 @@
 	ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
 	ctx.RegisterModuleType("vndk_prebuilt_shared", VndkPrebuiltSharedFactory)
 
-	vendorSnapshotImageSingleton.init(ctx)
+	VendorSnapshotImageSingleton.Init(ctx)
 	recoverySnapshotImageSingleton.init(ctx)
 	ctx.RegisterSingletonType("vndk-snapshot", VndkSnapshotSingleton)
 	RegisterVndkLibraryTxtTypes(ctx)
@@ -685,3 +688,64 @@
 
 	return ctx
 }
+
+func checkSnapshotIncludeExclude(t *testing.T, ctx *android.TestContext, singleton android.TestingSingleton, moduleName, snapshotFilename, subDir, variant string, include bool, fake bool) {
+	t.Helper()
+	mod := ctx.ModuleForTests(moduleName, variant)
+	outputFiles := mod.OutputFiles(t, "")
+	if len(outputFiles) != 1 {
+		t.Errorf("%q must have single output\n", moduleName)
+		return
+	}
+	snapshotPath := filepath.Join(subDir, snapshotFilename)
+
+	if include {
+		out := singleton.Output(snapshotPath)
+		if fake {
+			if out.Rule == nil {
+				t.Errorf("Missing rule for module %q output file %q", moduleName, outputFiles[0])
+			}
+		} else {
+			if out.Input.String() != outputFiles[0].String() {
+				t.Errorf("The input of snapshot %q must be %q, but %q", moduleName, out.Input.String(), outputFiles[0])
+			}
+		}
+	} else {
+		out := singleton.MaybeOutput(snapshotPath)
+		if out.Rule != nil {
+			t.Errorf("There must be no rule for module %q output file %q", moduleName, outputFiles[0])
+		}
+	}
+}
+
+func CheckSnapshot(t *testing.T, ctx *android.TestContext, singleton android.TestingSingleton, moduleName, snapshotFilename, subDir, variant string) {
+	t.Helper()
+	checkSnapshotIncludeExclude(t, ctx, singleton, moduleName, snapshotFilename, subDir, variant, true, false)
+}
+
+func CheckSnapshotExclude(t *testing.T, ctx *android.TestContext, singleton android.TestingSingleton, moduleName, snapshotFilename, subDir, variant string) {
+	t.Helper()
+	checkSnapshotIncludeExclude(t, ctx, singleton, moduleName, snapshotFilename, subDir, variant, false, false)
+}
+
+func CheckSnapshotRule(t *testing.T, ctx *android.TestContext, singleton android.TestingSingleton, moduleName, snapshotFilename, subDir, variant string) {
+	t.Helper()
+	checkSnapshotIncludeExclude(t, ctx, singleton, moduleName, snapshotFilename, subDir, variant, true, true)
+}
+
+func AssertExcludeFromVendorSnapshotIs(t *testing.T, ctx *android.TestContext, name string, expected bool, variant string) {
+	t.Helper()
+	m := ctx.ModuleForTests(name, variant).Module().(LinkableInterface)
+	if m.ExcludeFromVendorSnapshot() != expected {
+		t.Errorf("expected %q ExcludeFromVendorSnapshot to be %t", m.String(), expected)
+	}
+}
+
+func GetOutputPaths(ctx *android.TestContext, variant string, moduleNames []string) (paths android.Paths) {
+	for _, moduleName := range moduleNames {
+		module := ctx.ModuleForTests(moduleName, variant).Module().(*Module)
+		output := module.outputFile.Path().RelativeToTop()
+		paths = append(paths, output)
+	}
+	return paths
+}
diff --git a/cc/vendor_public_library_test.go b/cc/vendor_public_library_test.go
index 01959b4..769be09 100644
--- a/cc/vendor_public_library_test.go
+++ b/cc/vendor_public_library_test.go
@@ -79,7 +79,7 @@
 	// test if libsystem is linked to the stub
 	ld := ctx.ModuleForTests("libsystem", coreVariant).Rule("ld")
 	libflags := ld.Args["libFlags"]
-	stubPaths := getOutputPaths(ctx, coreVariant, []string{"libvendorpublic"})
+	stubPaths := GetOutputPaths(ctx, coreVariant, []string{"libvendorpublic"})
 	if !strings.Contains(libflags, stubPaths[0].String()) {
 		t.Errorf("libflags for libsystem must contain %#v, but was %#v", stubPaths[0], libflags)
 	}
@@ -87,7 +87,7 @@
 	// test if libsystem is linked to the stub
 	ld = ctx.ModuleForTests("libproduct", productVariant).Rule("ld")
 	libflags = ld.Args["libFlags"]
-	stubPaths = getOutputPaths(ctx, productVariant, []string{"libvendorpublic"})
+	stubPaths = GetOutputPaths(ctx, productVariant, []string{"libvendorpublic"})
 	if !strings.Contains(libflags, stubPaths[0].String()) {
 		t.Errorf("libflags for libproduct must contain %#v, but was %#v", stubPaths[0], libflags)
 	}
@@ -95,7 +95,8 @@
 	// test if libvendor is linked to the real shared lib
 	ld = ctx.ModuleForTests("libvendor", vendorVariant).Rule("ld")
 	libflags = ld.Args["libFlags"]
-	stubPaths = getOutputPaths(ctx, vendorVariant, []string{"libvendorpublic"})
+	stubPaths = GetOutputPaths(ctx, vendorVariant, []string{"libvendorpublic"})
+
 	if !strings.Contains(libflags, stubPaths[0].String()) {
 		t.Errorf("libflags for libvendor must contain %#v, but was %#v", stubPaths[0], libflags)
 	}
diff --git a/cc/vendor_snapshot.go b/cc/vendor_snapshot.go
index 4e59a95..5a303c8 100644
--- a/cc/vendor_snapshot.go
+++ b/cc/vendor_snapshot.go
@@ -31,7 +31,7 @@
 	"SOONG_VENDOR_SNAPSHOT_ZIP",
 	android.OptionalPath{},
 	true,
-	vendorSnapshotImageSingleton,
+	VendorSnapshotImageSingleton,
 	false, /* fake */
 }
 
@@ -40,7 +40,7 @@
 	"SOONG_VENDOR_FAKE_SNAPSHOT_ZIP",
 	android.OptionalPath{},
 	true,
-	vendorSnapshotImageSingleton,
+	VendorSnapshotImageSingleton,
 	true, /* fake */
 }
 
@@ -82,7 +82,7 @@
 	// Implementation of the image interface specific to the image
 	// associated with this snapshot (e.g., specific to the vendor image,
 	// recovery image, etc.).
-	image snapshotImage
+	image SnapshotImage
 
 	// Whether this singleton is for fake snapshot or not.
 	// Fake snapshot is a snapshot whose prebuilt binaries and headers are empty.
@@ -104,7 +104,7 @@
 	return RecoverySnapshotSingleton().(*snapshotSingleton).image.isProprietaryPath(dir, deviceConfig)
 }
 
-func isVendorProprietaryModule(ctx android.BaseModuleContext) bool {
+func IsVendorProprietaryModule(ctx android.BaseModuleContext) bool {
 	// Any module in a vendor proprietary path is a vendor proprietary
 	// module.
 	if isVendorProprietaryPath(ctx.ModuleDir(), ctx.DeviceConfig()) {
@@ -147,7 +147,7 @@
 }
 
 // Determines if the module is a candidate for snapshot.
-func isSnapshotAware(cfg android.DeviceConfig, m LinkableInterface, inProprietaryPath bool, apexInfo android.ApexInfo, image snapshotImage) bool {
+func isSnapshotAware(cfg android.DeviceConfig, m LinkableInterface, inProprietaryPath bool, apexInfo android.ApexInfo, image SnapshotImage) bool {
 	if !m.Enabled() || m.HiddenFromMake() {
 		return false
 	}
@@ -205,7 +205,7 @@
 		if sanitizable.Static() {
 			return sanitizable.OutputFile().Valid() && !image.private(m)
 		}
-		if sanitizable.Shared() {
+		if sanitizable.Shared() || sanitizable.Rlib() {
 			if !sanitizable.OutputFile().Valid() {
 				return false
 			}
@@ -393,6 +393,8 @@
 				libType = "static"
 			} else if m.Shared() {
 				libType = "shared"
+			} else if m.Rlib() {
+				libType = "rlib"
 			} else {
 				libType = "header"
 			}
@@ -404,7 +406,7 @@
 				libPath := m.OutputFile().Path()
 				stem = libPath.Base()
 				if sanitizable, ok := m.(PlatformSanitizeable); ok {
-					if sanitizable.Static() && sanitizable.SanitizePropDefined() && sanitizable.IsSanitizerEnabled(cfi) {
+					if (sanitizable.Static() || sanitizable.Rlib()) && sanitizable.SanitizePropDefined() && sanitizable.IsSanitizerEnabled(cfi) {
 						// both cfi and non-cfi variant for static libraries can exist.
 						// attach .cfi to distinguish between cfi and non-cfi.
 						// e.g. libbase.a -> libbase.cfi.a
diff --git a/cc/vendor_snapshot_test.go b/cc/vendor_snapshot_test.go
index c3b5e8c..ceffb29 100644
--- a/cc/vendor_snapshot_test.go
+++ b/cc/vendor_snapshot_test.go
@@ -108,27 +108,27 @@
 		// For shared libraries, only non-VNDK vendor_available modules are captured
 		sharedVariant := fmt.Sprintf("android_vendor.29_%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)
+		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)
+		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.29_%s_%s_static", archType, archVariant)
 		staticCfiVariant := fmt.Sprintf("android_vendor.29_%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)
+		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"),
@@ -142,8 +142,8 @@
 		if archType == "arm64" {
 			binaryVariant := fmt.Sprintf("android_vendor.29_%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)
+			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"))
@@ -156,7 +156,7 @@
 		// For object modules, all vendor:true and vendor_available modules are captured.
 		objectVariant := fmt.Sprintf("android_vendor.29_%s_%s", archType, archVariant)
 		objectDir := filepath.Join(snapshotVariantPath, archDir, "object")
-		checkSnapshot(t, ctx, snapshotSingleton, "obj", "obj.o", objectDir, objectVariant)
+		CheckSnapshot(t, ctx, snapshotSingleton, "obj", "obj.o", objectDir, objectVariant)
 		jsonFiles = append(jsonFiles, filepath.Join(objectDir, "obj.o.json"))
 	}
 
@@ -239,15 +239,15 @@
 		sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
 
 		// Included modules
-		checkSnapshot(t, ctx, snapshotSingleton, "libvendor", "libvendor.so", sharedDir, sharedVariant)
+		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)
+		CheckSnapshot(t, ctx, snapshotSingleton, "prebuilt_libfoo", "libfoo.so", sharedDir, sharedVariant)
 		includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "libfoo.so.json"))
 
 		// Excluded modules. Modules not included in the directed vendor snapshot
 		// are still include as fake modules.
-		checkSnapshotRule(t, ctx, snapshotSingleton, "libvendor_available", "libvendor_available.so", sharedDir, sharedVariant)
+		CheckSnapshotRule(t, ctx, snapshotSingleton, "libvendor_available", "libvendor_available.so", sharedDir, sharedVariant)
 		includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "libvendor_available.so.json"))
 	}
 
@@ -839,10 +839,11 @@
 		[]string{staticVariant, "libvendor.vendor_static.31.arm64"},
 		[]string{staticVariant, "libvendor_without_snapshot"},
 	} {
-		outputPaths := getOutputPaths(ctx, input[0] /* variant */, []string{input[1]} /* module name */)
+		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
@@ -868,7 +869,7 @@
 	}
 
 	libclientCfiLdFlags := ctx.ModuleForTests("libclient_cfi", sharedCfiVariant).Rule("ld").Args["libFlags"]
-	libvendorCfiOutputPaths := getOutputPaths(ctx, staticCfiVariant, []string{"libvendor.vendor_static.31.arm64"})
+	libvendorCfiOutputPaths := GetOutputPaths(ctx, staticCfiVariant, []string{"libvendor.vendor_static.31.arm64"})
 	if !strings.Contains(libclientCfiLdFlags, libvendorCfiOutputPaths[0].String()) {
 		t.Errorf("libflags for libclientCfi must contain %#v, but was %#v", libvendorCfiOutputPaths[0], libclientCfiLdFlags)
 	}
@@ -881,7 +882,7 @@
 	}
 
 	binWithoutSnapshotLdFlags := ctx.ModuleForTests("bin_without_snapshot", binaryVariant).Rule("ld").Args["libFlags"]
-	libVndkStaticOutputPaths := getOutputPaths(ctx, staticVariant, []string{"libvndk.vendor_static.31.arm64"})
+	libVndkStaticOutputPaths := GetOutputPaths(ctx, staticVariant, []string{"libvndk.vendor_static.31.arm64"})
 	if !strings.Contains(binWithoutSnapshotLdFlags, libVndkStaticOutputPaths[0].String()) {
 		t.Errorf("libflags for bin_without_snapshot must contain %#v, but was %#v",
 			libVndkStaticOutputPaths[0], binWithoutSnapshotLdFlags)
@@ -1006,14 +1007,6 @@
 	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)
@@ -1081,13 +1074,13 @@
 	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)
+	AssertExcludeFromVendorSnapshotIs(t, ctx, "libinclude", false, vendorVariant)
+	AssertExcludeFromVendorSnapshotIs(t, ctx, "libexclude", true, vendorVariant)
+	AssertExcludeFromVendorSnapshotIs(t, ctx, "libavailable_exclude", true, vendorVariant)
 
 	// A vendor module is excluded, but by its path, not the
 	// exclude_from_vendor_snapshot property.
-	assertExcludeFromVendorSnapshotIs(t, ctx, "libvendor", false)
+	AssertExcludeFromVendorSnapshotIs(t, ctx, "libvendor", false, vendorVariant)
 
 	// Verify the content of the vendor snapshot.
 
@@ -1110,15 +1103,15 @@
 		sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
 
 		// Included modules
-		checkSnapshot(t, ctx, snapshotSingleton, "libinclude", "libinclude.so", sharedDir, sharedVariant)
+		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)
+		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)
+		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)
+		CheckSnapshotExclude(t, ctx, snapshotSingleton, "libavailable_exclude", "libavailable_exclude.so", sharedDir, sharedVariant)
 		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "libavailable_exclude.so.json"))
 	}
 
@@ -1258,9 +1251,9 @@
 		// 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)
+		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"),
@@ -1269,9 +1262,9 @@
 		// 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)
+		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"),
@@ -1281,8 +1274,8 @@
 		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)
+			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"))
@@ -1295,7 +1288,7 @@
 		// 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)
+		CheckSnapshot(t, ctx, snapshotSingleton, "obj", "obj.o", objectDir, objectVariant)
 		jsonFiles = append(jsonFiles, filepath.Join(objectDir, "obj.o.json"))
 	}
 
@@ -1393,15 +1386,15 @@
 		sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
 
 		// Included modules
-		checkSnapshot(t, ctx, snapshotSingleton, "libinclude", "libinclude.so", sharedDir, sharedVariant)
+		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)
+		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)
+		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)
+		CheckSnapshotExclude(t, ctx, snapshotSingleton, "libavailable_exclude", "libavailable_exclude.so", sharedDir, sharedVariant)
 		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "libavailable_exclude.so.json"))
 	}
 
@@ -1482,15 +1475,15 @@
 		sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
 
 		// Included modules
-		checkSnapshot(t, ctx, snapshotSingleton, "librecovery", "librecovery.so", sharedDir, sharedVariant)
+		CheckSnapshot(t, ctx, snapshotSingleton, "librecovery", "librecovery.so", sharedDir, sharedVariant)
 		includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "librecovery.so.json"))
 		// Check that snapshot captures "prefer: true" prebuilt
-		checkSnapshot(t, ctx, snapshotSingleton, "prebuilt_libfoo", "libfoo.so", sharedDir, sharedVariant)
+		CheckSnapshot(t, ctx, snapshotSingleton, "prebuilt_libfoo", "libfoo.so", sharedDir, sharedVariant)
 		includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "libfoo.so.json"))
 
 		// Excluded modules. Modules not included in the directed recovery snapshot
 		// are still include as fake modules.
-		checkSnapshotRule(t, ctx, snapshotSingleton, "librecovery_available", "librecovery_available.so", sharedDir, sharedVariant)
+		CheckSnapshotRule(t, ctx, snapshotSingleton, "librecovery_available", "librecovery_available.so", sharedDir, sharedVariant)
 		includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "librecovery_available.so.json"))
 	}
 
diff --git a/cc/vndk.go b/cc/vndk.go
index 6a56c34..dd1c3e1 100644
--- a/cc/vndk.go
+++ b/cc/vndk.go
@@ -360,7 +360,7 @@
 	// prebuilt vndk modules should match with device
 	// TODO(b/142675459): Use enabled: to select target device in vndk_prebuilt_shared
 	// When b/142675459 is landed, remove following check
-	if p, ok := m.linker.(*vndkPrebuiltLibraryDecorator); ok && !p.matchesWithDevice(mctx.DeviceConfig()) {
+	if p, ok := m.linker.(*vndkPrebuiltLibraryDecorator); ok && !p.MatchesWithDevice(mctx.DeviceConfig()) {
 		return false
 	}
 
diff --git a/cc/vndk_prebuilt.go b/cc/vndk_prebuilt.go
index fc4412a..141f438 100644
--- a/cc/vndk_prebuilt.go
+++ b/cc/vndk_prebuilt.go
@@ -82,7 +82,7 @@
 }
 
 func (p *vndkPrebuiltLibraryDecorator) NameSuffix() string {
-	suffix := p.version()
+	suffix := p.Version()
 	if p.arch() != "" {
 		suffix += "." + p.arch()
 	}
@@ -92,7 +92,7 @@
 	return vndkSuffix + suffix
 }
 
-func (p *vndkPrebuiltLibraryDecorator) version() string {
+func (p *vndkPrebuiltLibraryDecorator) Version() string {
 	return String(p.properties.Version)
 }
 
@@ -107,7 +107,7 @@
 	return "64"
 }
 
-func (p *vndkPrebuiltLibraryDecorator) snapshotAndroidMkSuffix() string {
+func (p *vndkPrebuiltLibraryDecorator) SnapshotAndroidMkSuffix() string {
 	return ".vendor"
 }
 
@@ -133,7 +133,7 @@
 func (p *vndkPrebuiltLibraryDecorator) link(ctx ModuleContext,
 	flags Flags, deps PathDeps, objs Objects) android.Path {
 
-	if !p.matchesWithDevice(ctx.DeviceConfig()) {
+	if !p.MatchesWithDevice(ctx.DeviceConfig()) {
 		ctx.Module().HideFromMake()
 		return nil
 	}
@@ -163,7 +163,7 @@
 		p.androidMkSuffix = p.NameSuffix()
 
 		vndkVersion := ctx.DeviceConfig().VndkVersion()
-		if vndkVersion == p.version() {
+		if vndkVersion == p.Version() {
 			p.androidMkSuffix = ""
 		}
 
@@ -184,7 +184,7 @@
 	return nil
 }
 
-func (p *vndkPrebuiltLibraryDecorator) matchesWithDevice(config android.DeviceConfig) bool {
+func (p *vndkPrebuiltLibraryDecorator) MatchesWithDevice(config android.DeviceConfig) bool {
 	arches := config.Arches()
 	if len(arches) == 0 || arches[0].ArchType.String() != p.arch() {
 		return false
@@ -202,7 +202,7 @@
 	return false
 }
 
-func (p *vndkPrebuiltLibraryDecorator) isSnapshotPrebuilt() bool {
+func (p *vndkPrebuiltLibraryDecorator) IsSnapshotPrebuilt() bool {
 	return true
 }
 
diff --git a/jar/jar.go b/jar/jar.go
index a8f06a4..f164ee1 100644
--- a/jar/jar.go
+++ b/jar/jar.go
@@ -77,7 +77,7 @@
 		Name:  MetaDir,
 		Extra: []byte{MetaDirExtra[1], MetaDirExtra[0], 0, 0},
 	}
-	dirHeader.SetMode(0700 | os.ModeDir)
+	dirHeader.SetMode(0755 | os.ModeDir)
 	dirHeader.SetModTime(DefaultTime)
 
 	return dirHeader
@@ -95,7 +95,7 @@
 		Method:             zip.Store,
 		UncompressedSize64: uint64(len(b)),
 	}
-	fh.SetMode(0700)
+	fh.SetMode(0644)
 	fh.SetModTime(DefaultTime)
 
 	return fh, b, nil
diff --git a/java/droidstubs.go b/java/droidstubs.go
index 17c7a7b..3607082 100644
--- a/java/droidstubs.go
+++ b/java/droidstubs.go
@@ -397,7 +397,17 @@
 	rule.Command().Text("rm -rf").Flag(homeDir.String())
 	rule.Command().Text("mkdir -p").Flag(homeDir.String())
 
-	cmd := rule.Command()
+	var cmd *android.RuleBuilderCommand
+	if len(sourcepaths) > 0 {
+		// We are passing the sourcepaths as an argument to metalava below, but the directories may
+		// not exist already (if they do not contain any listed inputs for metalava). Note that this
+		// is in a rule.SboxInputs()rule, so we are not modifying the actual source tree by creating
+		// these directories.
+		cmd = rule.Command()
+		cmd.Text("mkdir -p").Flags(cmd.PathsForInputs(sourcepaths))
+	}
+
+	cmd = rule.Command()
 	cmd.FlagWithArg("ANDROID_PREFS_ROOT=", homeDir.String())
 
 	if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_METALAVA") {
@@ -431,6 +441,7 @@
 	}
 
 	if len(sourcepaths) > 0 {
+		// TODO(b/153703940): Pass .html files to metalava and remove this argument.
 		cmd.FlagWithList("-sourcepath ", sourcepaths.Strings(), ":")
 	} else {
 		cmd.FlagWithArg("-sourcepath ", `""`)
diff --git a/rust/Android.bp b/rust/Android.bp
index b611672..11069d1 100644
--- a/rust/Android.bp
+++ b/rust/Android.bp
@@ -32,6 +32,7 @@
         "rust.go",
         "sanitize.go",
         "source_provider.go",
+        "snapshot_prebuilt.go",
         "snapshot_utils.go",
         "strip.go",
         "test.go",
@@ -53,6 +54,7 @@
         "rust_test.go",
         "source_provider_test.go",
         "test_test.go",
+        "vendor_snapshot_test.go",
     ],
     pluginFor: ["soong_build"],
 }
diff --git a/rust/binary.go b/rust/binary.go
index ffc0413..8d0a0a7 100644
--- a/rust/binary.go
+++ b/rust/binary.go
@@ -137,6 +137,9 @@
 	// Binaries default to dylib dependencies for device, rlib for host.
 	if binary.preferRlib() {
 		return rlibAutoDep
+	} else if mod, ok := ctx.Module().(*Module); ok && mod.InVendor() {
+		// Vendor Rust binaries should prefer rlibs.
+		return rlibAutoDep
 	} else if ctx.Device() {
 		return dylibAutoDep
 	} else {
@@ -147,6 +150,8 @@
 func (binary *binaryDecorator) stdLinkage(ctx *depsContext) RustLinkage {
 	if binary.preferRlib() {
 		return RlibLinkage
+	} else if ctx.RustModule().InVendor() {
+		return RlibLinkage
 	}
 	return binary.baseCompiler.stdLinkage(ctx)
 }
diff --git a/rust/image.go b/rust/image.go
index 900842e..3b54f12 100644
--- a/rust/image.go
+++ b/rust/image.go
@@ -82,7 +82,12 @@
 }
 
 func (mod *Module) SnapshotVersion(mctx android.BaseModuleContext) string {
-	panic("Rust modules do not support snapshotting: " + mod.BaseModuleName())
+	if snapshot, ok := mod.compiler.(cc.SnapshotInterface); ok {
+		return snapshot.Version()
+	} else {
+		mctx.ModuleErrorf("version is unknown for snapshot prebuilt")
+		return ""
+	}
 }
 
 func (mod *Module) VendorRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
@@ -110,7 +115,9 @@
 }
 
 func (mod *Module) IsSnapshotPrebuilt() bool {
-	// Rust does not support prebuilts in its snapshots
+	if p, ok := mod.compiler.(cc.SnapshotInterface); ok {
+		return p.IsSnapshotPrebuilt()
+	}
 	return false
 }
 
@@ -202,6 +209,8 @@
 
 func (mod *Module) ImageMutatorBegin(mctx android.BaseModuleContext) {
 	// Rust does not support installing to the product image yet.
+	vendorSpecific := mctx.SocSpecific() || mctx.DeviceSpecific()
+
 	if Bool(mod.VendorProperties.Product_available) {
 		mctx.PropertyErrorf("product_available",
 			"Rust modules do not yet support being available to the product image")
@@ -217,6 +226,11 @@
 			mctx.PropertyErrorf("vendor_ramdisk_available", "cannot be set for rust_ffi or rust_ffi_shared modules.")
 		}
 	}
+	if vendorSpecific {
+		if lib, ok := mod.compiler.(libraryInterface); ok && lib.buildDylib() {
+			mctx.PropertyErrorf("vendor", "Vendor-only dylibs are not yet supported, use rust_library_rlib.")
+		}
+	}
 
 	cc.MutateImage(mctx, mod)
 
diff --git a/rust/library.go b/rust/library.go
index 1bdf83a..1a56ef6 100644
--- a/rust/library.go
+++ b/rust/library.go
@@ -99,6 +99,8 @@
 	MutatedProperties LibraryMutatedProperties
 	includeDirs       android.Paths
 	sourceProvider    SourceProvider
+
+	collectedSnapshotHeaders android.Paths
 }
 
 type libraryInterface interface {
@@ -122,7 +124,8 @@
 	setStatic()
 	setSource()
 
-	// Set libstd linkage
+	// libstd linkage functions
+	rlibStd() bool
 	setRlibStd()
 	setDylibStd()
 
@@ -193,6 +196,10 @@
 	library.MutatedProperties.VariantIsShared = false
 }
 
+func (library *libraryDecorator) rlibStd() bool {
+	return library.MutatedProperties.VariantIsStaticStd
+}
+
 func (library *libraryDecorator) setRlibStd() {
 	library.MutatedProperties.VariantIsStaticStd = true
 }
@@ -220,7 +227,10 @@
 }
 
 func (library *libraryDecorator) autoDep(ctx android.BottomUpMutatorContext) autoDep {
-	if library.preferRlib() {
+	if ctx.Module().(*Module).InVendor() {
+		// Vendor modules should statically link libstd.
+		return rlibAutoDep
+	} else if library.preferRlib() {
 		return rlibAutoDep
 	} else if library.rlib() || library.static() {
 		return rlibAutoDep
@@ -236,7 +246,10 @@
 }
 
 func (library *libraryDecorator) stdLinkage(ctx *depsContext) RustLinkage {
-	if library.static() || library.MutatedProperties.VariantIsStaticStd {
+	if ctx.RustModule().InVendor() {
+		// Vendor modules should statically link libstd.
+		return RlibLinkage
+	} else if library.static() || library.MutatedProperties.VariantIsStaticStd {
 		return RlibLinkage
 	} else if library.baseCompiler.preferRlib() {
 		return RlibLinkage
@@ -623,6 +636,19 @@
 				// Disable dylib Vendor Ramdisk variations until we support these.
 				v.(*Module).Disable()
 			}
+
+			variation := v.(*Module).ModuleBase.ImageVariation().Variation
+			if strings.HasPrefix(variation, cc.VendorVariationPrefix) &&
+				m.HasVendorVariant() &&
+				!cc.IsVendorProprietaryModule(mctx) &&
+				strings.TrimPrefix(variation, cc.VendorVariationPrefix) == mctx.DeviceConfig().VndkVersion() {
+
+				// cc.MutateImage runs before LibraryMutator, so vendor variations which are meant for rlibs only are
+				// produced for Dylibs; however, dylibs should not be enabled for boardVndkVersion for
+				// non-vendor proprietary modules.
+				v.(*Module).Disable()
+			}
+
 		case "source":
 			v.(*Module).compiler.(libraryInterface).setSource()
 			// The source variant does not produce any library.
@@ -659,9 +685,10 @@
 				dylib := modules[1].(*Module)
 				rlib.compiler.(libraryInterface).setRlibStd()
 				dylib.compiler.(libraryInterface).setDylibStd()
-				if dylib.ModuleBase.ImageVariation().Variation == android.VendorRamdiskVariation {
+				if dylib.ModuleBase.ImageVariation().Variation == android.VendorRamdiskVariation ||
+					strings.HasPrefix(dylib.ModuleBase.ImageVariation().Variation, cc.VendorVariationPrefix) {
 					// TODO(b/165791368)
-					// Disable rlibs that link against dylib-std on vendor ramdisk variations until those dylib
+					// Disable rlibs that link against dylib-std on vendor and vendor ramdisk variations until those dylib
 					// variants are properly supported.
 					dylib.Disable()
 				}
@@ -671,3 +698,54 @@
 		}
 	}
 }
+
+func (l *libraryDecorator) snapshotHeaders() android.Paths {
+	if l.collectedSnapshotHeaders == nil {
+		panic("snapshotHeaders() must be called after collectHeadersForSnapshot()")
+	}
+	return l.collectedSnapshotHeaders
+}
+
+// collectHeadersForSnapshot collects all exported headers from library.
+// It globs header files in the source tree for exported include directories,
+// and tracks generated header files separately.
+//
+// This is to be called from GenerateAndroidBuildActions, and then collected
+// header files can be retrieved by snapshotHeaders().
+func (l *libraryDecorator) collectHeadersForSnapshot(ctx android.ModuleContext, deps PathDeps) {
+	ret := android.Paths{}
+
+	// Glob together the headers from the modules include_dirs property
+	for _, path := range android.CopyOfPaths(l.includeDirs) {
+		dir := path.String()
+		glob, err := ctx.GlobWithDeps(dir+"/**/*", nil)
+		if err != nil {
+			ctx.ModuleErrorf("glob failed: %#v", err)
+			return
+		}
+
+		for _, header := range glob {
+			// Filter out only the files with extensions that are headers.
+			found := false
+			for _, ext := range cc.HeaderExts {
+				if strings.HasSuffix(header, ext) {
+					found = true
+					break
+				}
+			}
+			if !found {
+				continue
+			}
+			ret = append(ret, android.PathForSource(ctx, header))
+		}
+	}
+
+	// Glob together the headers from C dependencies as well, starting with non-generated headers.
+	ret = append(ret, cc.GlobHeadersForSnapshot(ctx, append(android.CopyOfPaths(deps.depIncludePaths), deps.depSystemIncludePaths...))...)
+
+	// Collect generated headers from C dependencies.
+	ret = append(ret, cc.GlobGeneratedHeadersForSnapshot(ctx, deps.depGeneratedHeaders)...)
+
+	// TODO(185577950): If support for generated headers is added, they need to be collected here as well.
+	l.collectedSnapshotHeaders = ret
+}
diff --git a/rust/rust.go b/rust/rust.go
index 46c8f25..b8c8be5 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -85,6 +85,9 @@
 	VendorRamdiskVariantNeeded bool     `blueprint:"mutated"`
 	ExtraVariants              []string `blueprint:"mutated"`
 
+	// Used by vendor snapshot to record dependencies from snapshot modules.
+	SnapshotSharedLibs []string `blueprint:"mutated"`
+
 	// Make this module available when building for vendor ramdisk.
 	// On device without a dedicated recovery partition, the module is only
 	// available after switching root into
@@ -92,6 +95,20 @@
 	// the recovery variant instead (TODO(b/165791368) recovery not yet supported)
 	Vendor_ramdisk_available *bool
 
+	// Normally Soong uses the directory structure to decide which modules
+	// should be included (framework) or excluded (non-framework) from the
+	// different snapshots (vendor, recovery, etc.), but this property
+	// allows a partner to exclude a module normally thought of as a
+	// framework module from the vendor snapshot.
+	Exclude_from_vendor_snapshot *bool
+
+	// Normally Soong uses the directory structure to decide which modules
+	// should be included (framework) or excluded (non-framework) from the
+	// different snapshots (vendor, recovery, etc.), but this property
+	// allows a partner to exclude a module normally thought of as a
+	// framework module from the recovery snapshot.
+	Exclude_from_recovery_snapshot *bool
+
 	// Minimum sdk version that the artifact should support when it runs as part of mainline modules(APEX).
 	Min_sdk_version *string
 
@@ -826,6 +843,14 @@
 
 		mod.docTimestampFile = mod.compiler.rustdoc(ctx, flags, deps)
 
+		// glob exported headers for snapshot, if BOARD_VNDK_VERSION is current or
+		// RECOVERY_SNAPSHOT_VERSION is current.
+		if lib, ok := mod.compiler.(snapshotLibraryInterface); ok {
+			if cc.ShouldCollectHeadersForSnapshot(ctx, mod, apexInfo) {
+				lib.collectHeadersForSnapshot(ctx, deps)
+			}
+		}
+
 		apexInfo := actx.Provider(android.ApexInfoProvider).(android.ApexInfo)
 		if mod.installable(apexInfo) {
 			mod.compiler.install(ctx)
@@ -1056,6 +1081,10 @@
 				depPaths.depClangFlags = append(depPaths.depClangFlags, exportedInfo.Flags...)
 				depPaths.depGeneratedHeaders = append(depPaths.depGeneratedHeaders, exportedInfo.GeneratedHeaders...)
 				directSharedLibDeps = append(directSharedLibDeps, ccDep)
+
+				// Record baseLibName for snapshots.
+				mod.Properties.SnapshotSharedLibs = append(mod.Properties.SnapshotSharedLibs, cc.BaseLibName(depName))
+
 				mod.Properties.AndroidMkSharedLibs = append(mod.Properties.AndroidMkSharedLibs, makeLibName)
 				exportDep = true
 			case cc.IsHeaderDepTag(depTag):
@@ -1161,6 +1190,11 @@
 
 	deps := mod.deps(ctx)
 	var commonDepVariations []blueprint.Variation
+	var snapshotInfo *cc.SnapshotInfo
+
+	if ctx.Os() == android.Android {
+		deps.SharedLibs, _ = cc.RewriteLibs(mod, &snapshotInfo, actx, ctx.Config(), deps.SharedLibs)
+	}
 
 	stdLinkage := "dylib-std"
 	if mod.compiler.stdLinkage(ctx) == RlibLinkage {
@@ -1168,61 +1202,101 @@
 	}
 
 	rlibDepVariations := commonDepVariations
+
 	if lib, ok := mod.compiler.(libraryInterface); !ok || !lib.sysroot() {
 		rlibDepVariations = append(rlibDepVariations,
 			blueprint.Variation{Mutator: "rust_stdlinkage", Variation: stdLinkage})
 	}
 
-	actx.AddVariationDependencies(
-		append(rlibDepVariations, []blueprint.Variation{
-			{Mutator: "rust_libraries", Variation: rlibVariation}}...),
-		rlibDepTag, deps.Rlibs...)
+	// rlibs
+	for _, lib := range deps.Rlibs {
+		depTag := rlibDepTag
+		lib = cc.RewriteSnapshotLib(lib, cc.GetSnapshot(mod, &snapshotInfo, actx).Rlibs)
+
+		actx.AddVariationDependencies(append(rlibDepVariations, []blueprint.Variation{
+			{Mutator: "rust_libraries", Variation: rlibVariation},
+		}...), depTag, lib)
+	}
+
+	// dylibs
 	actx.AddVariationDependencies(
 		append(commonDepVariations, []blueprint.Variation{
 			{Mutator: "rust_libraries", Variation: dylibVariation}}...),
 		dylibDepTag, deps.Dylibs...)
 
+	// rustlibs
 	if deps.Rustlibs != nil && !mod.compiler.Disabled() {
 		autoDep := mod.compiler.(autoDeppable).autoDep(ctx)
 		if autoDep.depTag == rlibDepTag {
-			actx.AddVariationDependencies(
-				append(rlibDepVariations, blueprint.Variation{Mutator: "rust_libraries", Variation: autoDep.variation}),
-				autoDep.depTag, deps.Rustlibs...)
+			for _, lib := range deps.Rustlibs {
+				depTag := autoDep.depTag
+				lib = cc.RewriteSnapshotLib(lib, cc.GetSnapshot(mod, &snapshotInfo, actx).Rlibs)
+				actx.AddVariationDependencies(append(rlibDepVariations, []blueprint.Variation{
+					{Mutator: "rust_libraries", Variation: autoDep.variation},
+				}...), depTag, lib)
+			}
 		} else {
 			actx.AddVariationDependencies(
 				append(commonDepVariations, blueprint.Variation{Mutator: "rust_libraries", Variation: autoDep.variation}),
 				autoDep.depTag, deps.Rustlibs...)
 		}
 	}
+
+	// stdlibs
 	if deps.Stdlibs != nil {
 		if mod.compiler.stdLinkage(ctx) == RlibLinkage {
-			actx.AddVariationDependencies(
-				append(commonDepVariations, blueprint.Variation{Mutator: "rust_libraries", Variation: "rlib"}),
-				rlibDepTag, deps.Stdlibs...)
+			for _, lib := range deps.Stdlibs {
+				depTag := rlibDepTag
+				lib = cc.RewriteSnapshotLib(lib, cc.GetSnapshot(mod, &snapshotInfo, actx).Rlibs)
+
+				actx.AddVariationDependencies(append(commonDepVariations, []blueprint.Variation{{Mutator: "rust_libraries", Variation: "rlib"}}...),
+					depTag, lib)
+			}
 		} else {
 			actx.AddVariationDependencies(
 				append(commonDepVariations, blueprint.Variation{Mutator: "rust_libraries", Variation: "dylib"}),
 				dylibDepTag, deps.Stdlibs...)
 		}
 	}
-	actx.AddVariationDependencies(append(commonDepVariations,
-		blueprint.Variation{Mutator: "link", Variation: "shared"}),
-		cc.SharedDepTag(), deps.SharedLibs...)
-	actx.AddVariationDependencies(append(commonDepVariations,
-		blueprint.Variation{Mutator: "link", Variation: "static"}),
-		cc.StaticDepTag(false), deps.StaticLibs...)
-	actx.AddVariationDependencies(append(commonDepVariations,
-		blueprint.Variation{Mutator: "link", Variation: "static"}),
-		cc.StaticDepTag(true), deps.WholeStaticLibs...)
+
+	for _, lib := range deps.SharedLibs {
+		depTag := cc.SharedDepTag()
+		name, version := cc.StubsLibNameAndVersion(lib)
+
+		variations := []blueprint.Variation{
+			{Mutator: "link", Variation: "shared"},
+		}
+		cc.AddSharedLibDependenciesWithVersions(ctx, mod, variations, depTag, name, version, false)
+	}
+
+	for _, lib := range deps.WholeStaticLibs {
+		depTag := cc.StaticDepTag(true)
+		lib = cc.RewriteSnapshotLib(lib, cc.GetSnapshot(mod, &snapshotInfo, actx).StaticLibs)
+
+		actx.AddVariationDependencies([]blueprint.Variation{
+			{Mutator: "link", Variation: "static"},
+		}, depTag, lib)
+	}
+
+	for _, lib := range deps.StaticLibs {
+		depTag := cc.StaticDepTag(false)
+		lib = cc.RewriteSnapshotLib(lib, cc.GetSnapshot(mod, &snapshotInfo, actx).StaticLibs)
+
+		actx.AddVariationDependencies([]blueprint.Variation{
+			{Mutator: "link", Variation: "static"},
+		}, depTag, lib)
+	}
 
 	actx.AddVariationDependencies(nil, cc.HeaderDepTag(), deps.HeaderLibs...)
 
 	crtVariations := cc.GetCrtVariations(ctx, mod)
 	if deps.CrtBegin != "" {
-		actx.AddVariationDependencies(crtVariations, cc.CrtBeginDepTag, deps.CrtBegin)
+		actx.AddVariationDependencies(crtVariations, cc.CrtBeginDepTag,
+			cc.RewriteSnapshotLib(deps.CrtBegin, cc.GetSnapshot(mod, &snapshotInfo, actx).Objects))
 	}
 	if deps.CrtEnd != "" {
-		actx.AddVariationDependencies(crtVariations, cc.CrtEndDepTag, deps.CrtEnd)
+		actx.AddVariationDependencies(crtVariations, cc.CrtEndDepTag,
+			cc.RewriteSnapshotLib(deps.CrtEnd, cc.GetSnapshot(mod, &snapshotInfo, actx).Objects))
 	}
 
 	if mod.sourceProvider != nil {
@@ -1232,6 +1306,7 @@
 				bindgen.Properties.Custom_bindgen)
 		}
 	}
+
 	// proc_macros are compiler plugins, and so we need the host arch variant as a dependendcy.
 	actx.AddFarVariationDependencies(ctx.Config().BuildOSTarget.Variations(), procMacroDepTag, deps.ProcMacros...)
 }
diff --git a/rust/rust_test.go b/rust/rust_test.go
index 6ae05d9..80f693e 100644
--- a/rust/rust_test.go
+++ b/rust/rust_test.go
@@ -37,21 +37,29 @@
 
 	genrule.PrepareForTestWithGenRuleBuildComponents,
 
-	PrepareForIntegrationTestWithRust,
+	PrepareForTestWithRustIncludeVndk,
+	android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+		variables.DeviceVndkVersion = StringPtr("current")
+		variables.ProductVndkVersion = StringPtr("current")
+		variables.Platform_vndk_version = StringPtr("29")
+	}),
 )
 
 var rustMockedFiles = android.MockFS{
-	"foo.rs":          nil,
-	"foo.c":           nil,
-	"src/bar.rs":      nil,
-	"src/any.h":       nil,
-	"proto.proto":     nil,
-	"proto/buf.proto": nil,
-	"buf.proto":       nil,
-	"foo.proto":       nil,
-	"liby.so":         nil,
-	"libz.so":         nil,
-	"data.txt":        nil,
+	"foo.rs":                       nil,
+	"foo.c":                        nil,
+	"src/bar.rs":                   nil,
+	"src/any.h":                    nil,
+	"c_includes/c_header.h":        nil,
+	"rust_includes/rust_headers.h": nil,
+	"proto.proto":                  nil,
+	"proto/buf.proto":              nil,
+	"buf.proto":                    nil,
+	"foo.proto":                    nil,
+	"liby.so":                      nil,
+	"libz.so":                      nil,
+	"data.txt":                     nil,
+	"liblog.map.txt":               nil,
 }
 
 // testRust returns a TestContext in which a basic environment has been setup.
@@ -67,19 +75,33 @@
 }
 
 func testRustVndk(t *testing.T, bp string) *android.TestContext {
+	return testRustVndkFs(t, bp, rustMockedFiles)
+}
+
+const (
+	sharedVendorVariant = "android_vendor.29_arm64_armv8-a_shared"
+	rlibVendorVariant   = "android_vendor.29_arm64_armv8-a_rlib_rlib-std"
+)
+
+func testRustVndkFs(t *testing.T, bp string, fs android.MockFS) *android.TestContext {
+	return testRustVndkFsVersions(t, bp, fs, "current", "current", "29")
+}
+
+func testRustVndkFsVersions(t *testing.T, bp string, fs android.MockFS, device_version, product_version, vndk_version string) *android.TestContext {
 	skipTestIfOsNotSupported(t)
 	result := android.GroupFixturePreparers(
 		prepareForRustTest,
-		rustMockedFiles.AddToFixture(),
+		fs.AddToFixture(),
 		android.FixtureModifyProductVariables(
 			func(variables android.FixtureProductVariables) {
-				variables.DeviceVndkVersion = StringPtr("current")
-				variables.ProductVndkVersion = StringPtr("current")
-				variables.Platform_vndk_version = StringPtr("29")
+				variables.DeviceVndkVersion = StringPtr(device_version)
+				variables.ProductVndkVersion = StringPtr(product_version)
+				variables.Platform_vndk_version = StringPtr(vndk_version)
 			},
 		),
 	).RunTestWithBp(t, bp)
 	return result.TestContext
+
 }
 
 // testRustCov returns a TestContext in which a basic environment has been
@@ -115,10 +137,14 @@
 
 // testRustVndkError is similar to testRustError, but can be used to test VNDK-related errors.
 func testRustVndkError(t *testing.T, pattern string, bp string) {
+	testRustVndkFsError(t, pattern, bp, rustMockedFiles)
+}
+
+func testRustVndkFsError(t *testing.T, pattern string, bp string, fs android.MockFS) {
 	skipTestIfOsNotSupported(t)
 	android.GroupFixturePreparers(
 		prepareForRustTest,
-		rustMockedFiles.AddToFixture(),
+		fs.AddToFixture(),
 		android.FixtureModifyProductVariables(
 			func(variables android.FixtureProductVariables) {
 				variables.DeviceVndkVersion = StringPtr("current")
diff --git a/rust/snapshot_prebuilt.go b/rust/snapshot_prebuilt.go
new file mode 100644
index 0000000..2f54973
--- /dev/null
+++ b/rust/snapshot_prebuilt.go
@@ -0,0 +1,121 @@
+// Copyright 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 rust
+
+import (
+	"android/soong/android"
+	"android/soong/cc"
+	"github.com/google/blueprint/proptools"
+)
+
+const (
+	snapshotRlibSuffix = "_rlib."
+)
+
+type snapshotLibraryDecorator struct {
+	cc.BaseSnapshotDecorator
+	*libraryDecorator
+	properties          cc.SnapshotLibraryProperties
+	sanitizerProperties struct {
+		CfiEnabled bool `blueprint:"mutated"`
+
+		// Library flags for cfi variant.
+		Cfi cc.SnapshotLibraryProperties `android:"arch_variant"`
+	}
+}
+
+func init() {
+	registerRustSnapshotModules(android.InitRegistrationContext)
+}
+
+func registerRustSnapshotModules(ctx android.RegistrationContext) {
+	cc.VendorSnapshotImageSingleton.RegisterAdditionalModule(ctx,
+		"vendor_snapshot_rlib", VendorSnapshotRlibFactory)
+}
+
+func snapshotLibraryFactory(image cc.SnapshotImage, moduleSuffix string) (*Module, *snapshotLibraryDecorator) {
+	module, library := NewRustLibrary(android.DeviceSupported)
+
+	module.sanitize = nil
+	library.stripper.StripProperties.Strip.None = proptools.BoolPtr(true)
+
+	prebuilt := &snapshotLibraryDecorator{
+		libraryDecorator: library,
+	}
+
+	module.compiler = prebuilt
+
+	prebuilt.Init(module, image, moduleSuffix)
+	module.AddProperties(
+		&prebuilt.properties,
+		&prebuilt.sanitizerProperties,
+	)
+
+	return module, prebuilt
+}
+
+func (library *snapshotLibraryDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) android.Path {
+	var variant string
+	if library.static() {
+		variant = cc.SnapshotStaticSuffix
+	} else if library.shared() {
+		variant = cc.SnapshotSharedSuffix
+	} else if library.rlib() {
+		variant = cc.SnapshotRlibSuffix
+	}
+
+	if !library.dylib() {
+		// TODO(184042776): Remove this check when dylibs are supported in snapshots.
+		library.SetSnapshotAndroidMkSuffix(ctx, variant)
+	}
+
+	if !library.MatchesWithDevice(ctx.DeviceConfig()) {
+		return nil
+	}
+
+	return android.PathForModuleSrc(ctx, *library.properties.Src)
+}
+
+func (library *snapshotLibraryDecorator) rustdoc(ctx ModuleContext, flags Flags, deps PathDeps) android.OptionalPath {
+	return android.OptionalPath{}
+}
+
+// vendor_snapshot_rlib is a special prebuilt rlib library which is auto-generated by
+// development/vendor_snapshot/update.py. As a part of vendor snapshot, vendor_snapshot_rlib
+// overrides the vendor variant of the rust rlib library with the same name, if BOARD_VNDK_VERSION
+// is set.
+func VendorSnapshotRlibFactory() android.Module {
+	module, prebuilt := snapshotLibraryFactory(cc.VendorSnapshotImageSingleton, cc.SnapshotRlibSuffix)
+	prebuilt.libraryDecorator.BuildOnlyRlib()
+	prebuilt.libraryDecorator.setNoStdlibs()
+	return module.Init()
+}
+
+func (library *snapshotLibraryDecorator) MatchesWithDevice(config android.DeviceConfig) bool {
+	arches := config.Arches()
+	if len(arches) == 0 || arches[0].ArchType.String() != library.Arch() {
+		return false
+	}
+	if library.properties.Src == nil {
+		return false
+	}
+	return true
+}
+
+func (library *snapshotLibraryDecorator) IsSnapshotPrebuilt() bool {
+	return true
+}
+
+var _ cc.SnapshotInterface = (*snapshotLibraryDecorator)(nil)
diff --git a/rust/snapshot_utils.go b/rust/snapshot_utils.go
index 943c790..bd7ca7f 100644
--- a/rust/snapshot_utils.go
+++ b/rust/snapshot_utils.go
@@ -18,18 +18,33 @@
 	"android/soong/android"
 )
 
+// snapshotLibraryInterface is an interface for libraries captured to VNDK / vendor snapshots.
+type snapshotLibraryInterface interface {
+	libraryInterface
+
+	// collectHeadersForSnapshot is called in GenerateAndroidBuildActions for snapshot aware
+	// modules (See isSnapshotAware below).
+	// This function should gather all headers needed for snapshot.
+	collectHeadersForSnapshot(ctx android.ModuleContext, deps PathDeps)
+
+	// snapshotHeaders should return collected headers by collectHeadersForSnapshot.
+	// Calling snapshotHeaders before collectHeadersForSnapshot is an error.
+	snapshotHeaders() android.Paths
+}
+
 func (mod *Module) ExcludeFromVendorSnapshot() bool {
-	// TODO Rust does not yet support snapshotting
-	return false
+	return Bool(mod.Properties.Exclude_from_vendor_snapshot)
 }
 
 func (mod *Module) ExcludeFromRecoverySnapshot() bool {
-	// TODO Rust does not yet support snapshotting
-	return false
+	return Bool(mod.Properties.Exclude_from_recovery_snapshot)
 }
 
 func (mod *Module) IsSnapshotLibrary() bool {
-	// TODO Rust does not yet support snapshotting
+	if lib, ok := mod.compiler.(libraryInterface); ok {
+		// Rust-native dylibs are not snapshot supported yet. Only snapshot the rlib-std variants of rlibs.
+		return lib.shared() || lib.static() || (lib.rlib() && lib.rlibStd())
+	}
 	return false
 }
 
@@ -39,8 +54,7 @@
 }
 
 func (mod *Module) SnapshotSharedLibs() []string {
-	// TODO Rust does not yet support snapshotting
-	return []string{}
+	return mod.Properties.SnapshotSharedLibs
 }
 
 func (mod *Module) Symlinks() []string {
@@ -49,6 +63,8 @@
 }
 
 func (m *Module) SnapshotHeaders() android.Paths {
-	// TODO Rust does not yet support snapshotting
+	if l, ok := m.compiler.(snapshotLibraryInterface); ok {
+		return l.snapshotHeaders()
+	}
 	return android.Paths{}
 }
diff --git a/rust/testing.go b/rust/testing.go
index a0f86b2..72f87e1 100644
--- a/rust/testing.go
+++ b/rust/testing.go
@@ -45,6 +45,11 @@
 	PrepareForTestWithRustDefaultModules,
 )
 
+var PrepareForTestWithRustIncludeVndk = android.GroupFixturePreparers(
+	PrepareForIntegrationTestWithRust,
+	cc.PrepareForTestWithCcIncludeVndk,
+)
+
 func GatherRequiredDepsForTest() string {
 	bp := `
 		rust_prebuilt_library {
@@ -130,6 +135,9 @@
 			apex_available: ["//apex_available:platform", "//apex_available:anyapex"],
 			min_sdk_version: "29",
 			vendor_available: true,
+			llndk: {
+				symbol_file: "liblog.map.txt",
+			},
 		}
 		cc_library {
 			name: "libprotobuf-cpp-full",
@@ -240,4 +248,5 @@
 		ctx.BottomUp("rust_begin", BeginMutator).Parallel()
 	})
 	ctx.RegisterSingletonType("rust_project_generator", rustProjectGeneratorSingleton)
+	registerRustSnapshotModules(ctx)
 }
diff --git a/rust/vendor_snapshot_test.go b/rust/vendor_snapshot_test.go
new file mode 100644
index 0000000..815f80e
--- /dev/null
+++ b/rust/vendor_snapshot_test.go
@@ -0,0 +1,1015 @@
+// Copyright 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 rust
+
+import (
+	"fmt"
+	"path/filepath"
+	"reflect"
+	"strings"
+	"testing"
+
+	"android/soong/android"
+	"android/soong/cc"
+)
+
+func TestVendorSnapshotCapture(t *testing.T) {
+	bp := `
+	rust_ffi {
+		name: "libffivendor_available",
+		crate_name: "ffivendor_available",
+		srcs: ["lib.rs"],
+		vendor_available: true,
+		include_dirs: ["rust_headers/"],
+	}
+
+	rust_ffi {
+		name: "libffivendor",
+		crate_name: "ffivendor",
+		srcs: ["lib.rs"],
+		vendor: true,
+		include_dirs: ["rust_headers/"],
+	}
+
+	rust_library {
+		name: "librustvendor_available",
+		crate_name: "rustvendor_available",
+		srcs: ["lib.rs"],
+		vendor_available: true,
+		include_dirs: ["rust_headers/"],
+	}
+
+	rust_library_rlib {
+		name: "librustvendor",
+		crate_name: "rustvendor",
+		srcs: ["lib.rs"],
+		vendor: true,
+		include_dirs: ["rust_headers/"],
+	}
+
+	rust_binary {
+		name: "vendor_available_bin",
+		vendor_available: true,
+		srcs: ["srcs/lib.rs"],
+	}
+
+	rust_binary {
+		name: "vendor_bin",
+		vendor: true,
+		srcs: ["srcs/lib.rs"],
+	}
+    `
+	skipTestIfOsNotSupported(t)
+	result := android.GroupFixturePreparers(
+		prepareForRustTest,
+		rustMockedFiles.AddToFixture(),
+		android.FixtureModifyProductVariables(
+			func(variables android.FixtureProductVariables) {
+				variables.DeviceVndkVersion = StringPtr("current")
+				variables.Platform_vndk_version = StringPtr("29")
+			},
+		),
+	).RunTestWithBp(t, bp)
+	ctx := result.TestContext
+
+	// Check Vendor snapshot output.
+
+	snapshotDir := "vendor-snapshot"
+	snapshotVariantPath := filepath.Join("out/soong", 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.29_%s_%s_shared", archType, archVariant)
+		sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
+		cc.CheckSnapshot(t, ctx, snapshotSingleton, "libffivendor_available", "libffivendor_available.so", sharedDir, sharedVariant)
+		jsonFiles = append(jsonFiles,
+			filepath.Join(sharedDir, "libffivendor_available.so.json"))
+
+		// For static libraries, all vendor:true and vendor_available modules (including VNDK) are captured.
+		staticVariant := fmt.Sprintf("android_vendor.29_%s_%s_static", archType, archVariant)
+		staticDir := filepath.Join(snapshotVariantPath, archDir, "static")
+		cc.CheckSnapshot(t, ctx, snapshotSingleton, "libffivendor_available", "libffivendor_available.a", staticDir, staticVariant)
+		cc.CheckSnapshot(t, ctx, snapshotSingleton, "libffivendor", "libffivendor.a", staticDir, staticVariant)
+		jsonFiles = append(jsonFiles,
+			filepath.Join(staticDir, "libffivendor_available.a.json"))
+		jsonFiles = append(jsonFiles,
+			filepath.Join(staticDir, "libffivendor.a.json"))
+
+		// For rlib libraries, all vendor:true and vendor_available modules (including VNDK) are captured.
+		rlibVariant := fmt.Sprintf("android_vendor.29_%s_%s_rlib_rlib-std", archType, archVariant)
+		rlibDir := filepath.Join(snapshotVariantPath, archDir, "rlib")
+		cc.CheckSnapshot(t, ctx, snapshotSingleton, "librustvendor_available", "librustvendor_available.rlib", rlibDir, rlibVariant)
+		cc.CheckSnapshot(t, ctx, snapshotSingleton, "librustvendor", "librustvendor.rlib", rlibDir, rlibVariant)
+		jsonFiles = append(jsonFiles,
+			filepath.Join(rlibDir, "librustvendor_available.rlib.json"))
+		jsonFiles = append(jsonFiles,
+			filepath.Join(rlibDir, "librustvendor.rlib.json"))
+
+		// For binary executables, all vendor:true and vendor_available modules are captured.
+		if archType == "arm64" {
+			binaryVariant := fmt.Sprintf("android_vendor.29_%s_%s", archType, archVariant)
+			binaryDir := filepath.Join(snapshotVariantPath, archDir, "binary")
+			cc.CheckSnapshot(t, ctx, snapshotSingleton, "vendor_available_bin", "vendor_available_bin", binaryDir, binaryVariant)
+			cc.CheckSnapshot(t, ctx, snapshotSingleton, "vendor_bin", "vendor_bin", binaryDir, binaryVariant)
+			jsonFiles = append(jsonFiles,
+				filepath.Join(binaryDir, "vendor_available_bin.json"))
+			jsonFiles = append(jsonFiles,
+				filepath.Join(binaryDir, "vendor_bin.json"))
+		}
+	}
+
+	for _, jsonFile := range jsonFiles {
+		// verify all json files exist
+		if snapshotSingleton.MaybeOutput(jsonFile).Rule == nil {
+			t.Errorf("%q expected but not found; #%v", jsonFile, jsonFiles)
+		}
+	}
+
+	// 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 := `
+	rust_ffi_shared {
+		name: "libffivendor_available",
+		crate_name: "ffivendor_available",
+		srcs: ["lib.rs"],
+		vendor_available: true,
+	}
+
+	rust_library {
+		name: "librustvendor_available",
+		crate_name: "rustvendor_available",
+		srcs: ["lib.rs"],
+		vendor_available: true,
+	}
+
+	rust_ffi_shared {
+		name: "libffivendor_exclude",
+		crate_name: "ffivendor_exclude",
+		srcs: ["lib.rs"],
+		vendor_available: true,
+	}
+
+	rust_library {
+		name: "librustvendor_exclude",
+		crate_name: "rustvendor_exclude",
+		srcs: ["lib.rs"],
+		vendor_available: true,
+	}
+`
+	ctx := testRustVndk(t, bp)
+	ctx.Config().TestProductVariables.VendorSnapshotModules = make(map[string]bool)
+	ctx.Config().TestProductVariables.VendorSnapshotModules["librustvendor_available"] = true
+	ctx.Config().TestProductVariables.VendorSnapshotModules["libffivendor_available"] = true
+	ctx.Config().TestProductVariables.DirectedVendorSnapshot = true
+
+	// Check Vendor snapshot output.
+
+	snapshotDir := "vendor-snapshot"
+	snapshotVariantPath := filepath.Join("out/soong", snapshotDir, "arm64")
+	snapshotSingleton := ctx.SingletonForTests("vendor-snapshot")
+
+	var includeJsonFiles []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.29_%s_%s_shared", archType, archVariant)
+		rlibVariant := fmt.Sprintf("android_vendor.29_%s_%s_rlib_rlib-std", archType, archVariant)
+		sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
+		rlibDir := filepath.Join(snapshotVariantPath, archDir, "rlib")
+
+		// Included modules
+		cc.CheckSnapshot(t, ctx, snapshotSingleton, "librustvendor_available", "librustvendor_available.rlib", rlibDir, rlibVariant)
+		cc.CheckSnapshot(t, ctx, snapshotSingleton, "libffivendor_available", "libffivendor_available.so", sharedDir, sharedVariant)
+		includeJsonFiles = append(includeJsonFiles, filepath.Join(rlibDir, "librustvendor_available.rlib.json"))
+		includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "libffivendor_available.so.json"))
+
+		// Excluded modules. Modules not included in the directed vendor snapshot
+		// are still include as fake modules.
+		cc.CheckSnapshotRule(t, ctx, snapshotSingleton, "librustvendor_exclude", "librustvendor_exclude.rlib", rlibDir, rlibVariant)
+		cc.CheckSnapshotRule(t, ctx, snapshotSingleton, "libffivendor_exclude", "libffivendor_exclude.so", sharedDir, sharedVariant)
+		includeJsonFiles = append(includeJsonFiles, filepath.Join(rlibDir, "librustvendor_exclude.rlib.json"))
+		includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "libffivendor_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)
+		}
+	}
+}
+
+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 := `
+		rust_ffi_shared {
+			name: "libinclude",
+			crate_name: "include",
+			srcs: ["include.rs"],
+			vendor_available: true,
+		}
+
+		rust_ffi_shared {
+			name: "libexclude",
+			crate_name: "exclude",
+			srcs: ["exclude.rs"],
+			vendor: true,
+			exclude_from_vendor_snapshot: true,
+		}
+
+		rust_ffi_shared {
+			name: "libavailable_exclude",
+			crate_name: "available_exclude",
+			srcs: ["lib.rs"],
+			vendor_available: true,
+			exclude_from_vendor_snapshot: true,
+		}
+
+		rust_library {
+			name: "librust_include",
+			crate_name: "rust_include",
+			srcs: ["include.rs"],
+			vendor_available: true,
+		}
+
+		rust_library_rlib {
+			name: "librust_exclude",
+			crate_name: "rust_exclude",
+			srcs: ["exclude.rs"],
+			vendor: true,
+			exclude_from_vendor_snapshot: true,
+		}
+
+		rust_library {
+			name: "librust_available_exclude",
+			crate_name: "rust_available_exclude",
+			srcs: ["lib.rs"],
+			vendor_available: true,
+			exclude_from_vendor_snapshot: true,
+		}
+	`
+
+	mockFS := map[string][]byte{
+		"framework/Android.bp": []byte(frameworkBp),
+		"framework/include.rs": nil,
+		"framework/exclude.rs": nil,
+	}
+
+	ctx := testRustVndkFs(t, "", mockFS)
+
+	// Test an include and exclude framework module.
+	cc.AssertExcludeFromVendorSnapshotIs(t, ctx, "libinclude", false, sharedVendorVariant)
+	cc.AssertExcludeFromVendorSnapshotIs(t, ctx, "libexclude", true, sharedVendorVariant)
+	cc.AssertExcludeFromVendorSnapshotIs(t, ctx, "libavailable_exclude", true, sharedVendorVariant)
+
+	cc.AssertExcludeFromVendorSnapshotIs(t, ctx, "librust_include", false, rlibVendorVariant)
+	cc.AssertExcludeFromVendorSnapshotIs(t, ctx, "librust_exclude", true, rlibVendorVariant)
+	cc.AssertExcludeFromVendorSnapshotIs(t, ctx, "librust_available_exclude", true, rlibVendorVariant)
+
+	// Verify the content of the vendor snapshot.
+
+	snapshotDir := "vendor-snapshot"
+	snapshotVariantPath := filepath.Join("out/soong", 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.29_%s_%s_shared", archType, archVariant)
+		sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
+		rlibVariant := fmt.Sprintf("android_vendor.29_%s_%s_rlib_rlib-std", archType, archVariant)
+		rlibDir := filepath.Join(snapshotVariantPath, archDir, "rlib")
+
+		// Included modules
+		cc.CheckSnapshot(t, ctx, snapshotSingleton, "libinclude", "libinclude.so", sharedDir, sharedVariant)
+		includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "libinclude.so.json"))
+		cc.CheckSnapshot(t, ctx, snapshotSingleton, "librust_include", "librust_include.rlib", rlibDir, rlibVariant)
+		includeJsonFiles = append(includeJsonFiles, filepath.Join(rlibDir, "librust_include.rlib.json"))
+
+		// Excluded modules
+		cc.CheckSnapshotExclude(t, ctx, snapshotSingleton, "libexclude", "libexclude.so", sharedDir, sharedVariant)
+		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "libexclude.so.json"))
+		cc.CheckSnapshotExclude(t, ctx, snapshotSingleton, "libavailable_exclude", "libavailable_exclude.so", sharedDir, sharedVariant)
+		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "libavailable_exclude.so.json"))
+		cc.CheckSnapshotExclude(t, ctx, snapshotSingleton, "librust_exclude", "librust_exclude.rlib", rlibDir, rlibVariant)
+		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(rlibDir, "librust_exclude.rlib.json"))
+		cc.CheckSnapshotExclude(t, ctx, snapshotSingleton, "librust_available_exclude", "librust_available_exclude.rlib", rlibDir, rlibVariant)
+		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(rlibDir, "librust_available_exclude.rlib.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,
+	}
+
+	cc_library {
+		name: "libvendor",
+		vendor: true,
+		nocrt: true,
+		no_libcrt: true,
+		stl: "none",
+		system_shared_libs: [],
+	}
+
+	cc_library {
+		name: "libvendor_available",
+		vendor_available: true,
+		nocrt: true,
+		no_libcrt: true,
+		stl: "none",
+		system_shared_libs: [],
+	}
+
+	cc_library {
+		name: "lib32",
+		vendor: true,
+		nocrt: true,
+		no_libcrt: true,
+		stl: "none",
+		system_shared_libs: [],
+		compile_multilib: "32",
+	}
+
+	cc_library {
+		name: "lib64",
+		vendor: true,
+		nocrt: true,
+		no_libcrt: true,
+		stl: "none",
+		system_shared_libs: [],
+		compile_multilib: "64",
+	}
+
+	rust_binary {
+		name: "bin",
+		vendor: true,
+		srcs: ["bin.rs"],
+	}
+
+	rust_binary {
+		name: "bin32",
+		vendor: true,
+		compile_multilib: "32",
+		srcs: ["bin.rs"],
+	}
+`
+
+	vndkBp := `
+	vndk_prebuilt_shared {
+		name: "libvndk",
+		version: "30",
+		target_arch: "arm64",
+		vendor_available: true,
+		product_available: true,
+		vndk: {
+			enabled: true,
+		},
+		arch: {
+			arm64: {
+				srcs: ["libvndk.so"],
+				export_include_dirs: ["include/libvndk"],
+			},
+			arm: {
+				srcs: ["libvndk.so"],
+				export_include_dirs: ["include/libvndk"],
+			},
+		},
+	}
+
+	// old snapshot module which has to be ignored
+	vndk_prebuilt_shared {
+		name: "libvndk",
+		version: "26",
+		target_arch: "arm64",
+		vendor_available: true,
+		product_available: true,
+		vndk: {
+			enabled: true,
+		},
+		arch: {
+			arm64: {
+				srcs: ["libvndk.so"],
+				export_include_dirs: ["include/libvndk"],
+			},
+			arm: {
+				srcs: ["libvndk.so"],
+				export_include_dirs: ["include/libvndk"],
+			},
+		},
+	}
+
+	// different arch snapshot which has to be ignored
+	vndk_prebuilt_shared {
+		name: "libvndk",
+		version: "30",
+		target_arch: "arm",
+		vendor_available: true,
+		product_available: true,
+		vndk: {
+			enabled: true,
+		},
+		arch: {
+			arm: {
+				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: [],
+	}
+
+	rust_library {
+		name: "librust_vendor_available",
+		crate_name: "rust_vendor",
+		vendor_available: true,
+		srcs: ["client.rs"],
+	}
+
+	rust_ffi_shared {
+		name: "libclient",
+		crate_name: "client",
+		vendor: true,
+		shared_libs: ["libvndk", "libvendor_available"],
+		static_libs: ["libvendor", "libvendor_without_snapshot"],
+		rustlibs: ["librust_vendor_available"],
+		arch: {
+			arm64: {
+				shared_libs: ["lib64"],
+			},
+			arm: {
+				shared_libs: ["lib32"],
+			},
+		},
+		srcs: ["client.rs"],
+	}
+
+	rust_library_rlib {
+		name: "libclient_rust",
+		crate_name: "client_rust",
+		vendor: true,
+		shared_libs: ["libvndk", "libvendor_available"],
+		static_libs: ["libvendor", "libvendor_without_snapshot"],
+		rustlibs: ["librust_vendor_available"],
+		arch: {
+			arm64: {
+				shared_libs: ["lib64"],
+			},
+			arm: {
+				shared_libs: ["lib32"],
+			},
+		},
+		srcs: ["client.rs"],
+	}
+
+	rust_binary {
+		name: "bin_without_snapshot",
+		vendor: true,
+		static_libs: ["libvndk"],
+		srcs: ["bin.rs"],
+		rustlibs: ["librust_vendor_available"],
+	}
+
+	vendor_snapshot {
+		name: "vendor_snapshot",
+		version: "30",
+		arch: {
+			arm64: {
+				vndk_libs: [
+					"libvndk",
+				],
+				static_libs: [
+					"libvendor",
+					"libvndk",
+					"libclang_rt.builtins-aarch64-android",
+				],
+				shared_libs: [
+					"libvendor_available",
+					"lib64",
+				],
+				rlibs: [
+					"libstd",
+					"libtest",
+					"librust_vendor_available",
+				],
+				binaries: [
+					"bin",
+				],
+                objects: [
+				    "crtend_so",
+					"crtbegin_so",
+					"crtbegin_dynamic",
+					"crtend_android"
+				],
+			},
+			arm: {
+				vndk_libs: [
+					"libvndk",
+				],
+				static_libs: [
+					"libvendor",
+					"libvndk",
+					"libclang_rt.builtins-arm-android",
+				],
+				shared_libs: [
+					"libvendor_available",
+					"lib32",
+				],
+				rlibs: [
+					"libstd",
+					"libtest",
+					"librust_vendor_available",
+				],
+				binaries: [
+					"bin32",
+				],
+                objects: [
+				    "crtend_so",
+					"crtbegin_so",
+					"crtbegin_dynamic",
+					"crtend_android"
+				],
+
+			},
+		}
+	}
+
+	vendor_snapshot_object {
+		name: "crtend_so",
+		version: "30",
+		target_arch: "arm64",
+		vendor: true,
+		stl: "none",
+		crt: true,
+		arch: {
+			arm64: {
+				src: "crtend_so.o",
+			},
+			arm: {
+				src: "crtend_so.o",
+			},
+		},
+	}
+
+	vendor_snapshot_object {
+		name: "crtbegin_so",
+		version: "30",
+		target_arch: "arm64",
+		vendor: true,
+		stl: "none",
+		crt: true,
+		arch: {
+			arm64: {
+				src: "crtbegin_so.o",
+			},
+			arm: {
+				src: "crtbegin_so.o",
+			},
+		},
+	}
+
+	vendor_snapshot_rlib {
+		name: "libstd",
+		version: "30",
+		target_arch: "arm64",
+		vendor: true,
+		sysroot: true,
+		arch: {
+			arm64: {
+				src: "libstd.rlib",
+			},
+			arm: {
+				src: "libstd.rlib",
+			},
+		},
+	}
+
+	vendor_snapshot_rlib {
+		name: "libtest",
+		version: "30",
+		target_arch: "arm64",
+		vendor: true,
+		sysroot: true,
+		arch: {
+			arm64: {
+				src: "libtest.rlib",
+			},
+			arm: {
+				src: "libtest.rlib",
+			},
+		},
+	}
+
+	vendor_snapshot_rlib {
+		name: "librust_vendor_available",
+		version: "30",
+		target_arch: "arm64",
+		vendor: true,
+		arch: {
+			arm64: {
+				src: "librust_vendor_available.rlib",
+			},
+			arm: {
+				src: "librust_vendor_available.rlib",
+			},
+		},
+	}
+
+	vendor_snapshot_object {
+		name: "crtend_android",
+		version: "30",
+		target_arch: "arm64",
+		vendor: true,
+		stl: "none",
+		crt: true,
+		arch: {
+			arm64: {
+				src: "crtend_so.o",
+			},
+			arm: {
+				src: "crtend_so.o",
+			},
+		},
+	}
+
+	vendor_snapshot_object {
+		name: "crtbegin_dynamic",
+		version: "30",
+		target_arch: "arm64",
+		vendor: true,
+		stl: "none",
+		crt: true,
+		arch: {
+			arm64: {
+				src: "crtbegin_so.o",
+			},
+			arm: {
+				src: "crtbegin_so.o",
+			},
+		},
+	}
+
+	vendor_snapshot_static {
+		name: "libvndk",
+		version: "30",
+		target_arch: "arm64",
+		compile_multilib: "both",
+		vendor: true,
+		arch: {
+			arm64: {
+				src: "libvndk.a",
+			},
+			arm: {
+				src: "libvndk.a",
+			},
+		},
+		shared_libs: ["libvndk"],
+		export_shared_lib_headers: ["libvndk"],
+	}
+
+	vendor_snapshot_static {
+		name: "libclang_rt.builtins-aarch64-android",
+		version: "30",
+		target_arch: "arm64",
+		vendor: true,
+		arch: {
+			arm64: {
+				src: "libclang_rt.builtins-aarch64-android.a",
+			},
+		},
+    }
+
+    vendor_snapshot_static {
+		name: "libclang_rt.builtins-arm-android",
+		version: "30",
+		target_arch: "arm64",
+		vendor: true,
+		arch: {
+			arm: {
+				src: "libclang_rt.builtins-arm-android.a",
+			},
+		},
+    }
+
+	vendor_snapshot_shared {
+		name: "lib32",
+		version: "30",
+		target_arch: "arm64",
+		compile_multilib: "32",
+		vendor: true,
+		arch: {
+			arm: {
+				src: "lib32.so",
+			},
+		},
+	}
+
+	vendor_snapshot_shared {
+		name: "lib64",
+		version: "30",
+		target_arch: "arm64",
+		compile_multilib: "64",
+		vendor: true,
+		arch: {
+			arm64: {
+				src: "lib64.so",
+			},
+		},
+	}
+	vendor_snapshot_shared {
+		name: "liblog",
+		version: "30",
+		target_arch: "arm64",
+		compile_multilib: "64",
+		vendor: true,
+		arch: {
+			arm64: {
+				src: "liblog.so",
+			},
+		},
+	}
+
+	vendor_snapshot_static {
+		name: "libvendor",
+		version: "30",
+		target_arch: "arm64",
+		compile_multilib: "both",
+		vendor: true,
+		arch: {
+			arm64: {
+				src: "libvendor.a",
+				export_include_dirs: ["include/libvendor"],
+			},
+			arm: {
+				src: "libvendor.a",
+				export_include_dirs: ["include/libvendor"],
+			},
+		},
+	}
+
+	vendor_snapshot_shared {
+		name: "libvendor_available",
+		version: "30",
+		target_arch: "arm64",
+		compile_multilib: "both",
+		vendor: true,
+		arch: {
+			arm64: {
+				src: "libvendor_available.so",
+				export_include_dirs: ["include/libvendor"],
+			},
+			arm: {
+				src: "libvendor_available.so",
+				export_include_dirs: ["include/libvendor"],
+			},
+		},
+	}
+
+	vendor_snapshot_binary {
+		name: "bin",
+		version: "30",
+		target_arch: "arm64",
+		compile_multilib: "64",
+		vendor: true,
+		arch: {
+			arm64: {
+				src: "bin",
+			},
+		},
+	}
+
+	vendor_snapshot_binary {
+		name: "bin32",
+		version: "30",
+		target_arch: "arm64",
+		compile_multilib: "32",
+		vendor: true,
+		arch: {
+			arm: {
+				src: "bin32",
+			},
+		},
+	}
+
+	// old snapshot module which has to be ignored
+	vendor_snapshot_binary {
+		name: "bin",
+		version: "26",
+		target_arch: "arm64",
+		compile_multilib: "first",
+		vendor: true,
+		arch: {
+			arm64: {
+				src: "bin",
+			},
+		},
+	}
+
+	// different arch snapshot which has to be ignored
+	vendor_snapshot_binary {
+		name: "bin",
+		version: "30",
+		target_arch: "arm",
+		compile_multilib: "first",
+		vendor: true,
+		arch: {
+			arm64: {
+				src: "bin",
+			},
+		},
+	}
+`
+
+	mockFS := android.MockFS{
+		"framework/Android.bp":                          []byte(frameworkBp),
+		"framework/bin.rs":                              nil,
+		"vendor/Android.bp":                             []byte(vendorProprietaryBp),
+		"vendor/bin":                                    nil,
+		"vendor/bin32":                                  nil,
+		"vendor/bin.rs":                                 nil,
+		"vendor/client.rs":                              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,
+		"vendor/lib32.so":                               nil,
+		"vendor/lib64.so":                               nil,
+		"vendor/liblog.so":                              nil,
+		"vendor/libstd.rlib":                            nil,
+		"vendor/libtest.rlib":                           nil,
+		"vendor/librust_vendor_available.rlib":          nil,
+		"vendor/crtbegin_so.o":                          nil,
+		"vendor/crtend_so.o":                            nil,
+		"vendor/libclang_rt.builtins-aarch64-android.a": nil,
+		"vendor/libclang_rt.builtins-arm-android.a":     nil,
+		"vndk/Android.bp":                               []byte(vndkBp),
+		"vndk/include/libvndk/a.h":                      nil,
+		"vndk/libvndk.so":                               nil,
+	}
+
+	sharedVariant := "android_vendor.30_arm64_armv8-a_shared"
+	rlibVariant := "android_vendor.30_arm64_armv8-a_rlib_rlib-std"
+	staticVariant := "android_vendor.30_arm64_armv8-a_static"
+	binaryVariant := "android_vendor.30_arm64_armv8-a"
+
+	shared32Variant := "android_vendor.30_arm_armv7-a-neon_shared"
+	binary32Variant := "android_vendor.30_arm_armv7-a-neon"
+
+	ctx := testRustVndkFsVersions(t, "", mockFS, "30", "current", "31")
+
+	// libclient uses libvndk.vndk.30.arm64, libvendor.vendor_static.30.arm64, libvendor_without_snapshot
+	libclientLdFlags := ctx.ModuleForTests("libclient", sharedVariant).Rule("rustc").Args["linkFlags"]
+	for _, input := range [][]string{
+		[]string{sharedVariant, "libvndk.vndk.30.arm64"},
+		[]string{staticVariant, "libvendor.vendor_static.30.arm64"},
+		[]string{staticVariant, "libvendor_without_snapshot"},
+	} {
+		outputPaths := cc.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", "lib64", "liblog.vendor", "libc.vendor", "libm.vendor", "libdl.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", "libclang_rt.builtins-aarch64-android.vendor"}; !reflect.DeepEqual(g, w) {
+		t.Errorf("wanted libclient AndroidMkStaticLibs %q, got %q", w, g)
+	}
+
+	libclientAndroidMkRlibs := ctx.ModuleForTests("libclient", sharedVariant).Module().(*Module).Properties.AndroidMkRlibs
+	if g, w := libclientAndroidMkRlibs, []string{"librust_vendor_available.vendor_rlib.30.arm64.rlib-std", "libstd.vendor_rlib.30.arm64", "libtest.vendor_rlib.30.arm64"}; !reflect.DeepEqual(g, w) {
+		t.Errorf("wanted libclient libclientAndroidMkRlibs %q, got %q", w, g)
+	}
+
+	libclientAndroidMkDylibs := ctx.ModuleForTests("libclient", sharedVariant).Module().(*Module).Properties.AndroidMkDylibs
+	if len(libclientAndroidMkDylibs) > 0 {
+		t.Errorf("wanted libclient libclientAndroidMkDylibs [], got %q", libclientAndroidMkDylibs)
+	}
+
+	libclient32AndroidMkSharedLibs := ctx.ModuleForTests("libclient", shared32Variant).Module().(*Module).Properties.AndroidMkSharedLibs
+	if g, w := libclient32AndroidMkSharedLibs, []string{"libvndk.vendor", "libvendor_available.vendor", "lib32", "liblog.vendor", "libc.vendor", "libm.vendor", "libdl.vendor"}; !reflect.DeepEqual(g, w) {
+		t.Errorf("wanted libclient32 AndroidMkSharedLibs %q, got %q", w, g)
+	}
+
+	libclientRustAndroidMkRlibs := ctx.ModuleForTests("libclient_rust", rlibVariant).Module().(*Module).Properties.AndroidMkRlibs
+	if g, w := libclientRustAndroidMkRlibs, []string{"librust_vendor_available.vendor_rlib.30.arm64.rlib-std", "libstd.vendor_rlib.30.arm64", "libtest.vendor_rlib.30.arm64"}; !reflect.DeepEqual(g, w) {
+		t.Errorf("wanted libclient libclientAndroidMkRlibs %q, got %q", w, g)
+	}
+
+	binWithoutSnapshotLdFlags := ctx.ModuleForTests("bin_without_snapshot", binaryVariant).Rule("rustc").Args["linkFlags"]
+	libVndkStaticOutputPaths := cc.GetOutputPaths(ctx, staticVariant, []string{"libvndk.vendor_static.30.arm64"})
+	if !strings.Contains(binWithoutSnapshotLdFlags, libVndkStaticOutputPaths[0].String()) {
+		t.Errorf("libflags for bin_without_snapshot must contain %#v, but was %#v",
+			libVndkStaticOutputPaths[0], binWithoutSnapshotLdFlags)
+	}
+
+	// bin is installed by bin.vendor_binary.30.arm64
+	ctx.ModuleForTests("bin.vendor_binary.30.arm64", binaryVariant).Output("bin")
+
+	// bin32 is installed by bin32.vendor_binary.30.arm64
+	ctx.ModuleForTests("bin32.vendor_binary.30.arm64", binary32Variant).Output("bin32")
+
+	// 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.30 variant
+	libvendorVariants := ctx.ModuleVariantsForTests("libvendor")
+	if android.InList(sharedVariant, libvendorVariants) {
+		t.Errorf("libvendor must not have variant %#v, but it does", sharedVariant)
+	}
+
+	libvendorAvailableVariants := ctx.ModuleVariantsForTests("libvendor_available")
+	if android.InList(sharedVariant, libvendorAvailableVariants) {
+		t.Errorf("libvendor_available must not have variant %#v, but it does", sharedVariant)
+	}
+
+	binVariants := ctx.ModuleVariantsForTests("bin")
+	if android.InList(binaryVariant, binVariants) {
+		t.Errorf("bin must not have variant %#v, but it does", sharedVariant)
+	}
+}
diff --git a/scripts/gen_sorted_bss_symbols.sh b/scripts/gen_sorted_bss_symbols.sh
deleted file mode 100755
index a9b61a1..0000000
--- a/scripts/gen_sorted_bss_symbols.sh
+++ /dev/null
@@ -1,28 +0,0 @@
-#!/bin/bash -e
-
-# Copyright 2019 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.
-
-# Script to generate a symbol ordering file that sorts bss section symbols by
-# their sizes.
-# Inputs:
-#  Environment:
-#   CLANG_BIN: path to the clang bin directory
-#  Arguments:
-#   $1: Input ELF file
-#   $2: Output symbol ordering file
-
-set -o pipefail
-
-${CLANG_BIN}/llvm-nm --size-sort $1 | awk '{if ($2 == "b" || $2 == "B") print $3}' > $2
diff --git a/zip/zip.go b/zip/zip.go
index 6e412c9..ae379f5 100644
--- a/zip/zip.go
+++ b/zip/zip.go
@@ -656,9 +656,9 @@
 			UncompressedSize64: uint64(fileSize),
 		}
 
-		mode := os.FileMode(0600)
+		mode := os.FileMode(0644)
 		if executable {
-			mode = 0700
+			mode = 0755
 		}
 		header.SetMode(mode)
 
@@ -955,7 +955,7 @@
 				dirHeader = &zip.FileHeader{
 					Name: cleanDir + "/",
 				}
-				dirHeader.SetMode(0700 | os.ModeDir)
+				dirHeader.SetMode(0755 | os.ModeDir)
 			}
 
 			dirHeader.SetModTime(z.time)
diff --git a/zip/zip_test.go b/zip/zip_test.go
index 441dea3..79cc0b4 100644
--- a/zip/zip_test.go
+++ b/zip/zip_test.go
@@ -62,7 +62,7 @@
 		Method:             method,
 		CRC32:              crc32.ChecksumIEEE(contents),
 		UncompressedSize64: uint64(len(contents)),
-		ExternalAttrs:      (syscall.S_IFREG | 0600) << 16,
+		ExternalAttrs:      (syscall.S_IFREG | 0644) << 16,
 	}
 }
 
@@ -72,7 +72,7 @@
 		Method:             zip.Store,
 		CRC32:              crc32.ChecksumIEEE(contents),
 		UncompressedSize64: uint64(len(contents)),
-		ExternalAttrs:      (syscall.S_IFREG | 0700) << 16,
+		ExternalAttrs:      (syscall.S_IFREG | 0644) << 16,
 	}
 }
 
@@ -92,7 +92,7 @@
 		Method:             zip.Store,
 		CRC32:              crc32.ChecksumIEEE(nil),
 		UncompressedSize64: 0,
-		ExternalAttrs:      (syscall.S_IFDIR|0700)<<16 | 0x10,
+		ExternalAttrs:      (syscall.S_IFDIR|0755)<<16 | 0x10,
 	}
 }