Merge "Use libabigail to track NDK ABIs."
diff --git a/android/arch.go b/android/arch.go
index f057342..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)
@@ -1885,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()
@@ -1923,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() ).
@@ -1951,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
@@ -1992,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 4967b12..8c68055 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -220,8 +220,7 @@
 	// Per-module denylist to opt modules out of mixed builds. Such modules will
 	// still be generated via bp2build.
 	mixedBuildsDisabledList = []string{
-		"libc_common_shared", // cparsons@ cc_library_static, breaks module `libc`.
-		"libc_nomalloc",      // cparsons@ cc_library_static, breaks module `linker`
+		// Currently empty, though should remain present to facilitate granular bp2build migration.
 	}
 
 	// Used for quicker lookups
diff --git a/android/bazel_handler.go b/android/bazel_handler.go
index a1206dc..f906c8a 100644
--- a/android/bazel_handler.go
+++ b/android/bazel_handler.go
@@ -351,6 +351,9 @@
 		"HOME="+paths.homeDir,
 		pwdPrefix(),
 		"BUILD_DIR="+absolutePath(paths.buildDir),
+		// Make OUT_DIR absolute here so tools/bazel.sh uses the correct
+		// OUT_DIR at <root>/out, instead of <root>/out/soong/workspace/out.
+		"OUT_DIR="+absolutePath(paths.outDir()),
 		// Disables local host detection of gcc; toolchain information is defined
 		// explicitly in BUILD files.
 		"BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1")
@@ -576,6 +579,11 @@
 	return filepath.Join(p.buildDir, "workspace")
 }
 
+// Returns the path to the top level out dir ($OUT_DIR).
+func (p *bazelPaths) outDir() string {
+	return filepath.Dir(p.buildDir)
+}
+
 // Issues commands to Bazel to receive results for all cquery requests
 // queued in the BazelContext.
 func (context *bazelContext) InvokeBazel() error {
diff --git a/android/filegroup.go b/android/filegroup.go
index fc6850e..e207412 100644
--- a/android/filegroup.go
+++ b/android/filegroup.go
@@ -63,7 +63,10 @@
 		Srcs: srcs,
 	}
 
-	props := bazel.BazelTargetModuleProperties{Rule_class: "filegroup"}
+	props := bazel.BazelTargetModuleProperties{
+		Rule_class:        "filegroup",
+		Bzl_load_location: "//build/bazel/rules:filegroup.bzl",
+	}
 
 	ctx.CreateBazelTargetModule(BazelFileGroupFactory, fg.Name(), props, attrs)
 }
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..6d235d6 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -479,18 +479,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)
 		}
 	}
 
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..df9c9bf
--- /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 b2d68da..c55de95 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,10 @@
 	Excludes []Label
 }
 
+func (ll *LabelList) IsNil() bool {
+	return ll.Includes == nil && ll.Excludes == nil
+}
+
 // uniqueParentDirectories returns a list of the unique parent directories for
 // all files in ll.Includes.
 func (ll *LabelList) uniqueParentDirectories() []string {
@@ -106,7 +109,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)
@@ -137,6 +160,32 @@
 	return strings
 }
 
+// Map a function over all labels in a LabelList.
+func MapLabelList(mapOver LabelList, mapFn func(string) string) LabelList {
+	var includes []Label
+	for _, inc := range mapOver.Includes {
+		mappedLabel := Label{Label: mapFn(inc.Label), OriginalModuleName: inc.OriginalModuleName}
+		includes = append(includes, mappedLabel)
+	}
+	// mapFn is not applied over excludes, but they are propagated as-is.
+	return LabelList{Includes: includes, Excludes: mapOver.Excludes}
+}
+
+// Map a function over all Labels in a LabelListAttribute
+func MapLabelListAttribute(mapOver LabelListAttribute, mapFn func(string) string) LabelListAttribute {
+	var result LabelListAttribute
+
+	result.Value = MapLabelList(mapOver.Value, mapFn)
+
+	for axis, configToLabels := range mapOver.ConfigurableValues {
+		for config, value := range configToLabels {
+			result.SetSelectValue(axis, config, MapLabelList(value, mapFn))
+		}
+	}
+
+	return result
+}
+
 // Return all needles in a given haystack, where needleFn is true for needles.
 func FilterLabelList(haystack LabelList, needleFn func(string) bool) LabelList {
 	var includes []Label
@@ -145,26 +194,20 @@
 			includes = append(includes, inc)
 		}
 	}
+	// needleFn is not applied over excludes, but they are propagated as-is.
 	return LabelList{Includes: includes, Excludes: haystack.Excludes}
 }
 
 // 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
@@ -172,24 +215,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
 }
 
@@ -234,489 +271,202 @@
 	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
-		}
-	}
-
-	for os := range PlatformOsMap {
-		if len(attrs.GetOsValueForTarget(os).Includes) > 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).Includes) > 0 {
-				return true
-			}
-		}
-	}
-	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))
-	}
+// HasConfigurableValues returns true if the attribute contains axis-specific label list values.
+func (lla LabelListAttribute) HasConfigurableValues() bool {
+	return len(lla.ConfigurableValues) > 0
 }
 
 // StringListAttribute corresponds to the string_list Bazel attribute type with
@@ -725,256 +475,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..bc556bf 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,7 +157,49 @@
 		},
 	}
 	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)
 		}
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 b87d713..c8ae031 100644
--- a/bp2build/cc_library_conversion_test.go
+++ b/bp2build/cc_library_conversion_test.go
@@ -47,6 +47,7 @@
 
 func registerCcLibraryModuleTypes(ctx android.RegistrationContext) {
 	cc.RegisterCCBuildComponents(ctx)
+	ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
 	ctx.RegisterModuleType("cc_library_static", cc.LibraryStaticFactory)
 	ctx.RegisterModuleType("toolchain_library", cc.ToolchainLibraryFactory)
 	ctx.RegisterModuleType("cc_library_headers", cc.LibraryHeaderFactory)
@@ -535,6 +536,139 @@
 	})
 }
 
+func TestCcLibrarySharedStaticPropsWithMixedSources(t *testing.T) {
+	runCcLibraryTestCase(t, bp2buildTestCase{
+		description:                        "cc_library shared/static props with c/cpp/s mixed sources",
+		moduleTypeUnderTest:                "cc_library",
+		moduleTypeUnderTestFactory:         cc.LibraryFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+		depsMutators:                       []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build},
+		dir:                                "foo/bar",
+		filesystem: map[string]string{
+			"foo/bar/both_source.cpp":   "",
+			"foo/bar/both_source.cc":    "",
+			"foo/bar/both_source.c":     "",
+			"foo/bar/both_source.s":     "",
+			"foo/bar/both_source.S":     "",
+			"foo/bar/shared_source.cpp": "",
+			"foo/bar/shared_source.cc":  "",
+			"foo/bar/shared_source.c":   "",
+			"foo/bar/shared_source.s":   "",
+			"foo/bar/shared_source.S":   "",
+			"foo/bar/static_source.cpp": "",
+			"foo/bar/static_source.cc":  "",
+			"foo/bar/static_source.c":   "",
+			"foo/bar/static_source.s":   "",
+			"foo/bar/static_source.S":   "",
+			"foo/bar/Android.bp": `
+cc_library {
+    name: "a",
+    srcs: [
+		"both_source.cpp",
+		"both_source.cc",
+		"both_source.c",
+		"both_source.s",
+		"both_source.S",
+        ":both_filegroup",
+	],
+    static: {
+		srcs: [
+			"static_source.cpp",
+			"static_source.cc",
+			"static_source.c",
+			"static_source.s",
+			"static_source.S",
+			":static_filegroup",
+		],
+    },
+    shared: {
+		srcs: [
+			"shared_source.cpp",
+			"shared_source.cc",
+			"shared_source.c",
+			"shared_source.s",
+			"shared_source.S",
+			":shared_filegroup",
+		],
+    },
+    bazel_module: { bp2build_available: true },
+}
+
+filegroup {
+    name: "both_filegroup",
+    srcs: [
+        // Not relevant, handled by filegroup macro
+	],
+}
+
+filegroup {
+    name: "shared_filegroup",
+    srcs: [
+        // Not relevant, handled by filegroup macro
+	],
+}
+
+filegroup {
+    name: "static_filegroup",
+    srcs: [
+        // Not relevant, handled by filegroup macro
+	],
+}
+`,
+		},
+		blueprint: soongCcLibraryPreamble,
+		expectedBazelTargets: []string{`cc_library(
+    name = "a",
+    copts = [
+        "-Ifoo/bar",
+        "-I$(BINDIR)/foo/bar",
+    ],
+    shared_srcs = [
+        ":shared_filegroup_cpp_srcs",
+        "shared_source.cc",
+        "shared_source.cpp",
+    ],
+    shared_srcs_as = [
+        "shared_source.s",
+        "shared_source.S",
+        ":shared_filegroup_as_srcs",
+    ],
+    shared_srcs_c = [
+        "shared_source.c",
+        ":shared_filegroup_c_srcs",
+    ],
+    srcs = [
+        ":both_filegroup_cpp_srcs",
+        "both_source.cc",
+        "both_source.cpp",
+    ],
+    srcs_as = [
+        "both_source.s",
+        "both_source.S",
+        ":both_filegroup_as_srcs",
+    ],
+    srcs_c = [
+        "both_source.c",
+        ":both_filegroup_c_srcs",
+    ],
+    static_srcs = [
+        ":static_filegroup_cpp_srcs",
+        "static_source.cc",
+        "static_source.cpp",
+    ],
+    static_srcs_as = [
+        "static_source.s",
+        "static_source.S",
+        ":static_filegroup_as_srcs",
+    ],
+    static_srcs_c = [
+        "static_source.c",
+        ":static_filegroup_c_srcs",
+    ],
+)`},
+	})
+}
+
 func TestCcLibraryNonConfiguredVersionScript(t *testing.T) {
 	runCcLibraryTestCase(t, bp2buildTestCase{
 		description:                        "cc_library non-configured version script",
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/build_test.bash b/build_test.bash
index 296a79c..b039285 100755
--- a/build_test.bash
+++ b/build_test.bash
@@ -54,7 +54,7 @@
 
 echo
 echo "Running Bazel smoke test..."
-"${TOP}/tools/bazel" --batch --max_idle_secs=1 info
+STANDALONE_BAZEL=true "${TOP}/tools/bazel" --batch --max_idle_secs=1 info
 
 echo
 echo "Running Soong test..."
diff --git a/cc/bp2build.go b/cc/bp2build.go
index fed9936..31e69d8 100644
--- a/cc/bp2build.go
+++ b/cc/bp2build.go
@@ -14,6 +14,7 @@
 package cc
 
 import (
+	"fmt"
 	"path/filepath"
 	"strings"
 
@@ -52,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 {
@@ -123,39 +95,21 @@
 
 		// 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)
-			}
-		}
-		// arch: { <arch>: static: { ... } }
-		for _, properties := range module.GetArchProperties(ctx, &StaticProperties{}) {
-			if p, ok := properties.(*StaticProperties); ok {
-				allDeps = appendDeps(allDeps, p.Static)
-			}
-		}
 	}
 
 	ctx.AddDependency(module, nil, android.SortedUniqueStrings(allDeps)...)
@@ -164,13 +118,86 @@
 // staticOrSharedAttributes are the Bazel-ified versions of StaticOrSharedProperties --
 // properties which apply to either the shared or static version of a cc_library module.
 type staticOrSharedAttributes struct {
-	copts            bazel.StringListAttribute
-	srcs             bazel.LabelListAttribute
+	srcs    bazel.LabelListAttribute
+	srcs_c  bazel.LabelListAttribute
+	srcs_as bazel.LabelListAttribute
+
+	copts bazel.StringListAttribute
+
 	staticDeps       bazel.LabelListAttribute
 	dynamicDeps      bazel.LabelListAttribute
 	wholeArchiveDeps bazel.LabelListAttribute
 }
 
+func groupSrcsByExtension(ctx android.TopDownMutatorContext, srcs bazel.LabelListAttribute) (cppSrcs, cSrcs, asSrcs bazel.LabelListAttribute) {
+	// Branch srcs into three language-specific groups.
+	// C++ is the "catch-all" group, and comprises generated sources because we don't
+	// know the language of these sources until the genrule is executed.
+	// TODO(b/190006308): Handle language detection of sources in a Bazel rule.
+	isCSrcOrFilegroup := func(s string) bool {
+		return strings.HasSuffix(s, ".c") || strings.HasSuffix(s, "_c_srcs")
+	}
+
+	isAsmSrcOrFilegroup := func(s string) bool {
+		return strings.HasSuffix(s, ".S") || strings.HasSuffix(s, ".s") || strings.HasSuffix(s, "_as_srcs")
+	}
+
+	// Check that a module is a filegroup type named <label>.
+	isFilegroupNamed := func(m android.Module, fullLabel string) bool {
+		if ctx.OtherModuleType(m) != "filegroup" {
+			return false
+		}
+		labelParts := strings.Split(fullLabel, ":")
+		if len(labelParts) > 2 {
+			// There should not be more than one colon in a label.
+			panic(fmt.Errorf("%s is not a valid Bazel label for a filegroup", fullLabel))
+		} else {
+			return m.Name() == labelParts[len(labelParts)-1]
+		}
+	}
+
+	// Convert the filegroup dependencies into the extension-specific filegroups
+	// filtered in the filegroup.bzl macro.
+	cppFilegroup := func(label string) string {
+		ctx.VisitDirectDeps(func(m android.Module) {
+			if isFilegroupNamed(m, label) {
+				label = label + "_cpp_srcs"
+				return
+			}
+		})
+		return label
+	}
+	cFilegroup := func(label string) string {
+		ctx.VisitDirectDeps(func(m android.Module) {
+			if isFilegroupNamed(m, label) {
+				label = label + "_c_srcs"
+				return
+			}
+		})
+		return label
+	}
+	asFilegroup := func(label string) string {
+		ctx.VisitDirectDeps(func(m android.Module) {
+			if isFilegroupNamed(m, label) {
+				label = label + "_as_srcs"
+				return
+			}
+		})
+		return label
+	}
+
+	cSrcs = bazel.MapLabelListAttribute(srcs, cFilegroup)
+	cSrcs = bazel.FilterLabelListAttribute(cSrcs, isCSrcOrFilegroup)
+
+	asSrcs = bazel.MapLabelListAttribute(srcs, asFilegroup)
+	asSrcs = bazel.FilterLabelListAttribute(asSrcs, isAsmSrcOrFilegroup)
+
+	cppSrcs = bazel.MapLabelListAttribute(srcs, cppFilegroup)
+	cppSrcs = bazel.SubtractBazelLabelListAttribute(cppSrcs, cSrcs)
+	cppSrcs = bazel.SubtractBazelLabelListAttribute(cppSrcs, asSrcs)
+	return
+}
+
 // bp2buildParseSharedProps returns the attributes for the shared variant of a cc_library.
 func bp2BuildParseSharedProps(ctx android.TopDownMutatorContext, module *Module) staticOrSharedAttributes {
 	lib, ok := module.compiler.(*libraryDecorator)
@@ -201,70 +228,43 @@
 
 	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)
 				}
 			}
 		}
 	}
 
+	cppSrcs, cSrcs, asSrcs := groupSrcsByExtension(ctx, attrs.srcs)
+	attrs.srcs = cppSrcs
+	attrs.srcs_c = cSrcs
+	attrs.srcs_as = asSrcs
+
 	return attrs
 }
 
@@ -284,45 +284,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,
 	}
@@ -422,7 +400,7 @@
 
 	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)
@@ -445,65 +423,45 @@
 		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))
+	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 {
+					srcsList := parseSrcs(baseCompilerProps)
+					srcs.SetSelectValue(axis, config, srcsList)
+					// The base srcs value should not contain any arch-specific excludes.
+					srcs.SetValue(bazel.SubtractBazelLabelList(srcs.Value, bazel.LabelList{Includes: srcsList.Excludes}))
+				}
+
+				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))
+			}
 		}
 	}
 
 	// 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))
+	for axis, configToProps := range archVariantCompilerProps {
+		for config, props := range configToProps {
+			if _, ok := props.(*BaseCompilerProperties); ok {
+				// TODO: handle non-arch
+				srcs.SetSelectValue(axis, config, bazel.SubtractBazelLabelList(srcs.SelectValue(axis, config), 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
-				if len(baseCompilerProps.Srcs) > 0 || len(baseCompilerProps.Exclude_srcs) > 0 {
-					srcs.SetOsArchValueForTarget(os.Name, arch.Name, bazel.SubtractBazelLabelList(srcsList, baseSrcsLabelList))
-				}
-				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))
-			}
-		}
+	for axis := range archVariantCompilerProps {
+		defaultsSrcs := bazel.SubtractBazelLabelList(baseSrcsLabelList, srcs.Value)
+		srcs.SetSelectValue(axis, bazel.ConditionsDefault, defaultsSrcs)
 	}
 
 	productVarPropNameToAttribute := map[string]*bazel.StringListAttribute{
@@ -520,28 +478,13 @@
 					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.ProductConfigVariable), prop.ProductConfigVariable, newFlags)
 			}
 		}
 	}
 
-	// Branch srcs into three language-specific groups.
-	// C++ is the "catch-all" group, and comprises generated sources because we don't
-	// know the language of these sources until the genrule is executed.
-	// TODO(b/): Handle language detection of sources in a Bazel rule.
-	isCSrc := func(s string) bool {
-		return strings.HasSuffix(s, ".c")
-	}
-	isAsmSrc := func(s string) bool {
-		return strings.HasSuffix(s, ".S") || strings.HasSuffix(s, ".s")
-	}
-	cSrcs := bazel.FilterLabelListAttribute(srcs, isCSrc)
-	asSrcs := bazel.FilterLabelListAttribute(srcs, isAsmSrc)
-	srcs = bazel.SubtractBazelLabelListAttribute(srcs, cSrcs)
-	srcs = bazel.SubtractBazelLabelListAttribute(srcs, asSrcs)
+	srcs, cSrcs, asSrcs := groupSrcsByExtension(ctx, srcs)
+
 	return compilerAttributes{
 		copts:      copts,
 		srcs:       srcs,
@@ -600,7 +543,7 @@
 			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
@@ -610,61 +553,23 @@
 		}
 	}
 
-	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))
-
-			if baseLinkerProps.Version_script != nil {
-				versionScript.SetValueForArch(arch.Name,
-					android.BazelLabelForModuleSrcSingle(ctx, *baseLinkerProps.Version_script))
-			}
-
-			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 {
+	for axis, configToProps := range module.GetArchVariantProperties(ctx, &BaseLinkerProperties{}) {
+		for config, props := range configToProps {
+			if baseLinkerProps, ok := props.(*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))
+				deps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, libs))
+				exportedDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, exportedLibs))
+				linkopts.SetSelectValue(axis, config, getBp2BuildLinkerFlags(baseLinkerProps))
+				wholeArchiveDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, wholeArchiveLibs))
 
 				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))
+				dynamicDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, sharedLibs))
 			}
 		}
 	}
@@ -733,27 +638,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 e4a52f1..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()),
@@ -2134,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"},
@@ -2154,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"},
@@ -2168,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
@@ -2200,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 {
@@ -2214,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{
@@ -2239,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)
@@ -2846,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(
@@ -2864,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:
@@ -2933,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)
@@ -2942,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
@@ -2950,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 aec2e4d..0ff78a5 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -221,25 +221,29 @@
 // For bp2build conversion.
 type bazelCcLibraryAttributes struct {
 	// Attributes pertaining to both static and shared variants.
-	Srcs                bazel.LabelListAttribute
+	Srcs    bazel.LabelListAttribute
+	Srcs_c  bazel.LabelListAttribute
+	Srcs_as bazel.LabelListAttribute
+
+	Copts      bazel.StringListAttribute
+	Cppflags   bazel.StringListAttribute
+	Conlyflags bazel.StringListAttribute
+	Asflags    bazel.StringListAttribute
+
 	Hdrs                bazel.LabelListAttribute
 	Deps                bazel.LabelListAttribute
 	Implementation_deps bazel.LabelListAttribute
 	Dynamic_deps        bazel.LabelListAttribute
 	Whole_archive_deps  bazel.LabelListAttribute
-	Copts               bazel.StringListAttribute
 	Includes            bazel.StringListAttribute
 	Linkopts            bazel.StringListAttribute
 
-	Cppflags   bazel.StringListAttribute
-	Srcs_c     bazel.LabelListAttribute
-	Conlyflags bazel.StringListAttribute
-	Srcs_as    bazel.LabelListAttribute
-	Asflags    bazel.StringListAttribute
-
 	// Attributes pertaining to shared variant.
-	Shared_copts                  bazel.StringListAttribute
-	Shared_srcs                   bazel.LabelListAttribute
+	Shared_srcs    bazel.LabelListAttribute
+	Shared_srcs_c  bazel.LabelListAttribute
+	Shared_srcs_as bazel.LabelListAttribute
+	Shared_copts   bazel.StringListAttribute
+
 	Exported_deps_for_shared      bazel.LabelListAttribute
 	Static_deps_for_shared        bazel.LabelListAttribute
 	Dynamic_deps_for_shared       bazel.LabelListAttribute
@@ -248,8 +252,11 @@
 	Version_script                bazel.LabelAttribute
 
 	// Attributes pertaining to static variant.
-	Static_copts                  bazel.StringListAttribute
-	Static_srcs                   bazel.LabelListAttribute
+	Static_srcs    bazel.LabelListAttribute
+	Static_srcs_c  bazel.LabelListAttribute
+	Static_srcs_as bazel.LabelListAttribute
+	Static_copts   bazel.StringListAttribute
+
 	Exported_deps_for_static      bazel.LabelListAttribute
 	Static_deps_for_static        bazel.LabelListAttribute
 	Dynamic_deps_for_static       bazel.LabelListAttribute
@@ -298,33 +305,38 @@
 	linkerAttrs := bp2BuildParseLinkerProps(ctx, m)
 	exportedIncludes := bp2BuildParseExportedIncludes(ctx, m)
 
-	var srcs bazel.LabelListAttribute
-	srcs.Append(compilerAttrs.srcs)
+	srcs := compilerAttrs.srcs
 
 	attrs := &bazelCcLibraryAttributes{
-		Srcs:                srcs,
+		Srcs:    srcs,
+		Srcs_c:  compilerAttrs.cSrcs,
+		Srcs_as: compilerAttrs.asSrcs,
+
+		Copts:      compilerAttrs.copts,
+		Cppflags:   compilerAttrs.cppFlags,
+		Conlyflags: compilerAttrs.conlyFlags,
+		Asflags:    compilerAttrs.asFlags,
+
 		Implementation_deps: linkerAttrs.deps,
 		Deps:                linkerAttrs.exportedDeps,
 		Dynamic_deps:        linkerAttrs.dynamicDeps,
 		Whole_archive_deps:  linkerAttrs.wholeArchiveDeps,
-		Copts:               compilerAttrs.copts,
 		Includes:            exportedIncludes,
 		Linkopts:            linkerAttrs.linkopts,
-		Cppflags:            compilerAttrs.cppFlags,
-		Srcs_c:              compilerAttrs.cSrcs,
-		Conlyflags:          compilerAttrs.conlyFlags,
-		Srcs_as:             compilerAttrs.asSrcs,
-		Asflags:             compilerAttrs.asFlags,
 
-		Shared_copts:                  sharedAttrs.copts,
 		Shared_srcs:                   sharedAttrs.srcs,
+		Shared_srcs_c:                 sharedAttrs.srcs_c,
+		Shared_srcs_as:                sharedAttrs.srcs_as,
+		Shared_copts:                  sharedAttrs.copts,
 		Static_deps_for_shared:        sharedAttrs.staticDeps,
 		Whole_archive_deps_for_shared: sharedAttrs.wholeArchiveDeps,
 		Dynamic_deps_for_shared:       sharedAttrs.dynamicDeps,
 		Version_script:                linkerAttrs.versionScript,
 
-		Static_copts:                  staticAttrs.copts,
 		Static_srcs:                   staticAttrs.srcs,
+		Static_srcs_c:                 staticAttrs.srcs_c,
+		Static_srcs_as:                staticAttrs.srcs_as,
+		Static_copts:                  staticAttrs.copts,
 		Static_deps_for_static:        staticAttrs.staticDeps,
 		Whole_archive_deps_for_static: staticAttrs.wholeArchiveDeps,
 		Dynamic_deps_for_static:       staticAttrs.dynamicDeps,
@@ -350,6 +362,7 @@
 		staticLibrarySdkMemberType,
 		staticAndSharedLibrarySdkMemberType,
 	}
+	module.bazelHandler = &ccLibraryBazelHandler{module: module}
 	return module.Init()
 }
 
@@ -358,7 +371,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()
 }
 
@@ -543,13 +556,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 {
@@ -559,18 +576,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))
@@ -606,20 +618,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()) {
@@ -635,7 +641,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, "/") {
@@ -653,7 +659,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 {
@@ -666,7 +672,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
@@ -679,15 +685,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
 }
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 a7351a9..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,69 +428,88 @@
 // }
 //
 // 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) {
-	coreVariations := append(ctx.Target().Variations(), blueprint.Variation{
+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.
+	variations := append(ctx.Target().Variations(), blueprint.Variation{
 		Mutator:   "image",
 		Variation: android.CoreVariation})
 
-	if ctx.OtherModuleFarDependencyVariantExists(coreVariations, ctx.Module().(*Module).BaseModuleName()) {
+	if ctx.OtherModuleFarDependencyVariantExists(variations, ctx.Module().(LinkableInterface).BaseModuleName()) {
 		p.baseProperties.Androidmk_suffix = p.image.moduleNameSuffix()
 		return
 	}
 
-	// If there is no matching core variation, there could still be a
-	// product variation, for example if a module is product specific and
-	// vendor available. In that case, we also want to add the androidmk
-	// suffix.
-
-	productVariations := append(ctx.Target().Variations(), blueprint.Variation{
+	variations = append(ctx.Target().Variations(), blueprint.Variation{
 		Mutator:   "image",
 		Variation: ProductVariationPrefix + ctx.DeviceConfig().PlatformVndkVersion()})
 
-	if ctx.OtherModuleFarDependencyVariantExists(productVariations, ctx.Module().(*Module).BaseModuleName()) {
+	if ctx.OtherModuleFarDependencyVariantExists(variations, ctx.Module().(LinkableInterface).BaseModuleName()) {
 		p.baseProperties.Androidmk_suffix = p.image.moduleNameSuffix()
 		return
 	}
 
+	images := []SnapshotImage{VendorSnapshotImageSingleton, recoverySnapshotImageSingleton}
+
+	for _, image := range images {
+		if p.image == image {
+			continue
+		}
+		variations = append(ctx.Target().Variations(), blueprint.Variation{
+			Mutator:   "image",
+			Variation: image.imageVariantName(ctx.DeviceConfig())})
+
+		if ctx.OtherModuleFarDependencyVariantExists(variations,
+			ctx.Module().(LinkableInterface).BaseModuleName()+
+				getSnapshotNameSuffix(
+					image.moduleNameSuffix()+variant,
+					p.Version(),
+					ctx.DeviceConfig().Arches()[0].ArchType.String())) {
+			p.baseProperties.Androidmk_suffix = p.image.moduleNameSuffix()
+			return
+		}
+	}
+
 	p.baseProperties.Androidmk_suffix = ""
 }
 
 // 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)
@@ -493,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
 	}
@@ -509,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"`
 
@@ -535,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"`
 	}
 }
 
@@ -551,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 {
@@ -566,7 +593,16 @@
 // As snapshots are prebuilts, this just returns the prebuilt binary after doing things which are
 // done by normal library decorator, e.g. exporting flags.
 func (p *snapshotLibraryDecorator) link(ctx ModuleContext, flags Flags, deps PathDeps, objs Objects) android.Path {
-	p.setSnapshotAndroidMkSuffix(ctx)
+	var variant string
+	if p.shared() {
+		variant = SnapshotSharedSuffix
+	} else if p.static() {
+		variant = SnapshotStaticSuffix
+	} else {
+		variant = snapshotHeaderSuffix
+	}
+
+	p.SetSnapshotAndroidMkSuffix(ctx, variant)
 
 	if p.header() {
 		return p.libraryDecorator.link(ctx, flags, deps, objs)
@@ -576,7 +612,7 @@
 		p.properties = p.sanitizerProperties.Cfi
 	}
 
-	if !p.matchesWithDevice(ctx.DeviceConfig()) {
+	if !p.MatchesWithDevice(ctx.DeviceConfig()) {
 		return nil
 	}
 
@@ -629,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)
 	}
 }
@@ -659,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
@@ -682,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,
@@ -696,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()
 }
@@ -706,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()
 }
@@ -716,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()
 }
@@ -726,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()
 }
@@ -736,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()
 }
@@ -766,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 {
@@ -784,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)
+	p.SetSnapshotAndroidMkSuffix(ctx, snapshotBinarySuffix)
 
-	if !p.matchesWithDevice(ctx.DeviceConfig()) {
+	if !p.MatchesWithDevice(ctx.DeviceConfig()) {
 		return nil
 	}
 
@@ -814,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
@@ -824,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)
@@ -843,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()
 }
@@ -861,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 {
@@ -879,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)
+	p.SetSnapshotAndroidMkSuffix(ctx, snapshotObjectSuffix)
 
-	if !p.matchesWithDevice(ctx.DeviceConfig()) {
+	if !p.MatchesWithDevice(ctx.DeviceConfig()) {
 		return nil
 	}
 
@@ -905,7 +941,7 @@
 	}
 	module.linker = prebuilt
 
-	prebuilt.init(module, vendorSnapshotImageSingleton, snapshotObjectSuffix)
+	prebuilt.Init(module, VendorSnapshotImageSingleton, snapshotObjectSuffix)
 	module.AddProperties(&prebuilt.properties)
 	return module.Init()
 }
@@ -923,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/java_test.go b/java/java_test.go
index 1b8aec2..bd373c1 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -19,7 +19,6 @@
 	"os"
 	"path/filepath"
 	"reflect"
-	"regexp"
 	"runtime"
 	"strconv"
 	"strings"
@@ -655,306 +654,6 @@
 	})
 }
 
-func TestJavaSdkLibraryImport(t *testing.T) {
-	result := prepareForJavaTest.RunTestWithBp(t, `
-		java_library {
-			name: "foo",
-			srcs: ["a.java"],
-			libs: ["sdklib"],
-			sdk_version: "current",
-		}
-
-		java_library {
-			name: "foo.system",
-			srcs: ["a.java"],
-			libs: ["sdklib"],
-			sdk_version: "system_current",
-		}
-
-		java_library {
-			name: "foo.test",
-			srcs: ["a.java"],
-			libs: ["sdklib"],
-			sdk_version: "test_current",
-		}
-
-		java_sdk_library_import {
-			name: "sdklib",
-			public: {
-				jars: ["a.jar"],
-			},
-			system: {
-				jars: ["b.jar"],
-			},
-			test: {
-				jars: ["c.jar"],
-				stub_srcs: ["c.java"],
-			},
-		}
-		`)
-
-	for _, scope := range []string{"", ".system", ".test"} {
-		fooModule := result.ModuleForTests("foo"+scope, "android_common")
-		javac := fooModule.Rule("javac")
-
-		sdklibStubsJar := result.ModuleForTests("sdklib.stubs"+scope, "android_common").Rule("combineJar").Output
-		android.AssertStringDoesContain(t, "foo classpath", javac.Args["classpath"], sdklibStubsJar.String())
-	}
-
-	CheckModuleDependencies(t, result.TestContext, "sdklib", "android_common", []string{
-		`prebuilt_sdklib.stubs`,
-		`prebuilt_sdklib.stubs.source.test`,
-		`prebuilt_sdklib.stubs.system`,
-		`prebuilt_sdklib.stubs.test`,
-	})
-}
-
-func TestJavaSdkLibraryImport_WithSource(t *testing.T) {
-	result := android.GroupFixturePreparers(
-		prepareForJavaTest,
-		PrepareForTestWithJavaSdkLibraryFiles,
-		FixtureWithLastReleaseApis("sdklib"),
-	).RunTestWithBp(t, `
-		java_sdk_library {
-			name: "sdklib",
-			srcs: ["a.java"],
-			sdk_version: "none",
-			system_modules: "none",
-			public: {
-				enabled: true,
-			},
-		}
-
-		java_sdk_library_import {
-			name: "sdklib",
-			public: {
-				jars: ["a.jar"],
-			},
-		}
-		`)
-
-	CheckModuleDependencies(t, result.TestContext, "sdklib", "android_common", []string{
-		`dex2oatd`,
-		`prebuilt_sdklib`,
-		`sdklib.impl`,
-		`sdklib.stubs`,
-		`sdklib.stubs.source`,
-		`sdklib.xml`,
-	})
-
-	CheckModuleDependencies(t, result.TestContext, "prebuilt_sdklib", "android_common", []string{
-		`prebuilt_sdklib.stubs`,
-		`sdklib.impl`,
-		// This should be prebuilt_sdklib.stubs but is set to sdklib.stubs because the
-		// dependency is added after prebuilts may have been renamed and so has to use
-		// the renamed name.
-		`sdklib.xml`,
-	})
-}
-
-func TestJavaSdkLibraryImport_Preferred(t *testing.T) {
-	result := android.GroupFixturePreparers(
-		prepareForJavaTest,
-		PrepareForTestWithJavaSdkLibraryFiles,
-		FixtureWithLastReleaseApis("sdklib"),
-	).RunTestWithBp(t, `
-		java_sdk_library {
-			name: "sdklib",
-			srcs: ["a.java"],
-			sdk_version: "none",
-			system_modules: "none",
-			public: {
-				enabled: true,
-			},
-		}
-
-		java_sdk_library_import {
-			name: "sdklib",
-			prefer: true,
-			public: {
-				jars: ["a.jar"],
-			},
-		}
-		`)
-
-	CheckModuleDependencies(t, result.TestContext, "sdklib", "android_common", []string{
-		`dex2oatd`,
-		`prebuilt_sdklib`,
-		`sdklib.impl`,
-		`sdklib.stubs`,
-		`sdklib.stubs.source`,
-		`sdklib.xml`,
-	})
-
-	CheckModuleDependencies(t, result.TestContext, "prebuilt_sdklib", "android_common", []string{
-		`prebuilt_sdklib.stubs`,
-		`sdklib.impl`,
-		`sdklib.xml`,
-	})
-}
-
-func TestJavaSdkLibraryEnforce(t *testing.T) {
-	partitionToBpOption := func(partition string) string {
-		switch partition {
-		case "system":
-			return ""
-		case "vendor":
-			return "soc_specific: true,"
-		case "product":
-			return "product_specific: true,"
-		default:
-			panic("Invalid partition group name: " + partition)
-		}
-	}
-
-	type testConfigInfo struct {
-		libraryType                string
-		fromPartition              string
-		toPartition                string
-		enforceVendorInterface     bool
-		enforceProductInterface    bool
-		enforceJavaSdkLibraryCheck bool
-		allowList                  []string
-	}
-
-	createPreparer := func(info testConfigInfo) android.FixturePreparer {
-		bpFileTemplate := `
-			java_library {
-				name: "foo",
-				srcs: ["foo.java"],
-				libs: ["bar"],
-				sdk_version: "current",
-				%s
-			}
-
-			%s {
-				name: "bar",
-				srcs: ["bar.java"],
-				sdk_version: "current",
-				%s
-			}
-		`
-
-		bpFile := fmt.Sprintf(bpFileTemplate,
-			partitionToBpOption(info.fromPartition),
-			info.libraryType,
-			partitionToBpOption(info.toPartition))
-
-		return android.GroupFixturePreparers(
-			PrepareForTestWithJavaSdkLibraryFiles,
-			FixtureWithLastReleaseApis("bar"),
-			android.FixtureWithRootAndroidBp(bpFile),
-			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-				variables.EnforceProductPartitionInterface = proptools.BoolPtr(info.enforceProductInterface)
-				if info.enforceVendorInterface {
-					variables.DeviceVndkVersion = proptools.StringPtr("current")
-				}
-				variables.EnforceInterPartitionJavaSdkLibrary = proptools.BoolPtr(info.enforceJavaSdkLibraryCheck)
-				variables.InterPartitionJavaLibraryAllowList = info.allowList
-			}),
-		)
-	}
-
-	runTest := func(t *testing.T, info testConfigInfo, expectedErrorPattern string) {
-		t.Run(fmt.Sprintf("%v", info), func(t *testing.T) {
-			errorHandler := android.FixtureExpectsNoErrors
-			if expectedErrorPattern != "" {
-				errorHandler = android.FixtureExpectsAtLeastOneErrorMatchingPattern(expectedErrorPattern)
-			}
-			android.GroupFixturePreparers(
-				prepareForJavaTest,
-				createPreparer(info),
-			).
-				ExtendWithErrorHandler(errorHandler).
-				RunTest(t)
-		})
-	}
-
-	errorMessage := "is not allowed across the partitions"
-
-	runTest(t, testConfigInfo{
-		libraryType:                "java_library",
-		fromPartition:              "product",
-		toPartition:                "system",
-		enforceVendorInterface:     true,
-		enforceProductInterface:    true,
-		enforceJavaSdkLibraryCheck: false,
-	}, "")
-
-	runTest(t, testConfigInfo{
-		libraryType:                "java_library",
-		fromPartition:              "product",
-		toPartition:                "system",
-		enforceVendorInterface:     true,
-		enforceProductInterface:    false,
-		enforceJavaSdkLibraryCheck: true,
-	}, "")
-
-	runTest(t, testConfigInfo{
-		libraryType:                "java_library",
-		fromPartition:              "product",
-		toPartition:                "system",
-		enforceVendorInterface:     true,
-		enforceProductInterface:    true,
-		enforceJavaSdkLibraryCheck: true,
-	}, errorMessage)
-
-	runTest(t, testConfigInfo{
-		libraryType:                "java_library",
-		fromPartition:              "vendor",
-		toPartition:                "system",
-		enforceVendorInterface:     true,
-		enforceProductInterface:    true,
-		enforceJavaSdkLibraryCheck: true,
-	}, errorMessage)
-
-	runTest(t, testConfigInfo{
-		libraryType:                "java_library",
-		fromPartition:              "vendor",
-		toPartition:                "system",
-		enforceVendorInterface:     true,
-		enforceProductInterface:    true,
-		enforceJavaSdkLibraryCheck: true,
-		allowList:                  []string{"bar"},
-	}, "")
-
-	runTest(t, testConfigInfo{
-		libraryType:                "java_library",
-		fromPartition:              "vendor",
-		toPartition:                "product",
-		enforceVendorInterface:     true,
-		enforceProductInterface:    true,
-		enforceJavaSdkLibraryCheck: true,
-	}, errorMessage)
-
-	runTest(t, testConfigInfo{
-		libraryType:                "java_sdk_library",
-		fromPartition:              "product",
-		toPartition:                "system",
-		enforceVendorInterface:     true,
-		enforceProductInterface:    true,
-		enforceJavaSdkLibraryCheck: true,
-	}, "")
-
-	runTest(t, testConfigInfo{
-		libraryType:                "java_sdk_library",
-		fromPartition:              "vendor",
-		toPartition:                "system",
-		enforceVendorInterface:     true,
-		enforceProductInterface:    true,
-		enforceJavaSdkLibraryCheck: true,
-	}, "")
-
-	runTest(t, testConfigInfo{
-		libraryType:                "java_sdk_library",
-		fromPartition:              "vendor",
-		toPartition:                "product",
-		enforceVendorInterface:     true,
-		enforceProductInterface:    true,
-		enforceJavaSdkLibraryCheck: true,
-	}, "")
-}
-
 func TestDefaults(t *testing.T) {
 	ctx, _ := testJava(t, `
 		java_defaults {
@@ -1411,521 +1110,6 @@
 	})
 }
 
-func TestJavaSdkLibrary(t *testing.T) {
-	result := android.GroupFixturePreparers(
-		prepareForJavaTest,
-		PrepareForTestWithJavaSdkLibraryFiles,
-		FixtureWithPrebuiltApis(map[string][]string{
-			"28": {"foo"},
-			"29": {"foo"},
-			"30": {"bar", "barney", "baz", "betty", "foo", "fred", "quuz", "wilma"},
-		}),
-	).RunTestWithBp(t, `
-		droiddoc_exported_dir {
-			name: "droiddoc-templates-sdk",
-			path: ".",
-		}
-		java_sdk_library {
-			name: "foo",
-			srcs: ["a.java", "b.java"],
-			api_packages: ["foo"],
-		}
-		java_sdk_library {
-			name: "bar",
-			srcs: ["a.java", "b.java"],
-			api_packages: ["bar"],
-		}
-		java_library {
-			name: "baz",
-			srcs: ["c.java"],
-			libs: ["foo", "bar.stubs"],
-			sdk_version: "system_current",
-		}
-		java_sdk_library {
-			name: "barney",
-			srcs: ["c.java"],
-			api_only: true,
-		}
-		java_sdk_library {
-			name: "betty",
-			srcs: ["c.java"],
-			shared_library: false,
-		}
-		java_sdk_library_import {
-		    name: "quuz",
-				public: {
-					jars: ["c.jar"],
-				},
-		}
-		java_sdk_library_import {
-		    name: "fred",
-				public: {
-					jars: ["b.jar"],
-				},
-		}
-		java_sdk_library_import {
-		    name: "wilma",
-				public: {
-					jars: ["b.jar"],
-				},
-				shared_library: false,
-		}
-		java_library {
-		    name: "qux",
-		    srcs: ["c.java"],
-		    libs: ["baz", "fred", "quuz.stubs", "wilma", "barney", "betty"],
-		    sdk_version: "system_current",
-		}
-		java_library {
-			name: "baz-test",
-			srcs: ["c.java"],
-			libs: ["foo"],
-			sdk_version: "test_current",
-		}
-		java_library {
-			name: "baz-29",
-			srcs: ["c.java"],
-			libs: ["foo"],
-			sdk_version: "system_29",
-		}
-		java_library {
-			name: "baz-module-30",
-			srcs: ["c.java"],
-			libs: ["foo"],
-			sdk_version: "module_30",
-		}
-		`)
-
-	// check the existence of the internal modules
-	result.ModuleForTests("foo", "android_common")
-	result.ModuleForTests(apiScopePublic.stubsLibraryModuleName("foo"), "android_common")
-	result.ModuleForTests(apiScopeSystem.stubsLibraryModuleName("foo"), "android_common")
-	result.ModuleForTests(apiScopeTest.stubsLibraryModuleName("foo"), "android_common")
-	result.ModuleForTests(apiScopePublic.stubsSourceModuleName("foo"), "android_common")
-	result.ModuleForTests(apiScopeSystem.stubsSourceModuleName("foo"), "android_common")
-	result.ModuleForTests(apiScopeTest.stubsSourceModuleName("foo"), "android_common")
-	result.ModuleForTests("foo"+sdkXmlFileSuffix, "android_common")
-	result.ModuleForTests("foo.api.public.28", "")
-	result.ModuleForTests("foo.api.system.28", "")
-	result.ModuleForTests("foo.api.test.28", "")
-
-	bazJavac := result.ModuleForTests("baz", "android_common").Rule("javac")
-	// tests if baz is actually linked to the stubs lib
-	android.AssertStringDoesContain(t, "baz javac classpath", bazJavac.Args["classpath"], "foo.stubs.system.jar")
-	// ... and not to the impl lib
-	android.AssertStringDoesNotContain(t, "baz javac classpath", bazJavac.Args["classpath"], "foo.jar")
-	// test if baz is not linked to the system variant of foo
-	android.AssertStringDoesNotContain(t, "baz javac classpath", bazJavac.Args["classpath"], "foo.stubs.jar")
-
-	bazTestJavac := result.ModuleForTests("baz-test", "android_common").Rule("javac")
-	// tests if baz-test is actually linked to the test stubs lib
-	android.AssertStringDoesContain(t, "baz-test javac classpath", bazTestJavac.Args["classpath"], "foo.stubs.test.jar")
-
-	baz29Javac := result.ModuleForTests("baz-29", "android_common").Rule("javac")
-	// tests if baz-29 is actually linked to the system 29 stubs lib
-	android.AssertStringDoesContain(t, "baz-29 javac classpath", baz29Javac.Args["classpath"], "prebuilts/sdk/29/system/foo.jar")
-
-	bazModule30Javac := result.ModuleForTests("baz-module-30", "android_common").Rule("javac")
-	// tests if "baz-module-30" is actually linked to the module 30 stubs lib
-	android.AssertStringDoesContain(t, "baz-module-30 javac classpath", bazModule30Javac.Args["classpath"], "prebuilts/sdk/30/module-lib/foo.jar")
-
-	// test if baz has exported SDK lib names foo and bar to qux
-	qux := result.ModuleForTests("qux", "android_common")
-	if quxLib, ok := qux.Module().(*Library); ok {
-		sdkLibs := quxLib.ClassLoaderContexts().UsesLibs()
-		android.AssertDeepEquals(t, "qux exports", []string{"foo", "bar", "fred", "quuz"}, sdkLibs)
-	}
-}
-
-func TestJavaSdkLibrary_StubOrImplOnlyLibs(t *testing.T) {
-	result := android.GroupFixturePreparers(
-		prepareForJavaTest,
-		PrepareForTestWithJavaSdkLibraryFiles,
-		FixtureWithLastReleaseApis("sdklib"),
-	).RunTestWithBp(t, `
-		java_sdk_library {
-			name: "sdklib",
-			srcs: ["a.java"],
-			libs: ["lib"],
-			static_libs: ["static-lib"],
-			impl_only_libs: ["impl-only-lib"],
-			stub_only_libs: ["stub-only-lib"],
-			stub_only_static_libs: ["stub-only-static-lib"],
-		}
-		java_defaults {
-			name: "defaults",
-			srcs: ["a.java"],
-			sdk_version: "current",
-		}
-		java_library { name: "lib", defaults: ["defaults"] }
-		java_library { name: "static-lib", defaults: ["defaults"] }
-		java_library { name: "impl-only-lib", defaults: ["defaults"] }
-		java_library { name: "stub-only-lib", defaults: ["defaults"] }
-		java_library { name: "stub-only-static-lib", defaults: ["defaults"] }
-		`)
-	var expectations = []struct {
-		lib               string
-		on_impl_classpath bool
-		on_stub_classpath bool
-		in_impl_combined  bool
-		in_stub_combined  bool
-	}{
-		{lib: "lib", on_impl_classpath: true},
-		{lib: "static-lib", in_impl_combined: true},
-		{lib: "impl-only-lib", on_impl_classpath: true},
-		{lib: "stub-only-lib", on_stub_classpath: true},
-		{lib: "stub-only-static-lib", in_stub_combined: true},
-	}
-	verify := func(sdklib, dep string, cp, combined bool) {
-		sdklibCp := result.ModuleForTests(sdklib, "android_common").Rule("javac").Args["classpath"]
-		expected := cp || combined // Every combined jar is also on the classpath.
-		android.AssertStringContainsEquals(t, "bad classpath for "+sdklib, sdklibCp, "/"+dep+".jar", expected)
-
-		combineJarInputs := result.ModuleForTests(sdklib, "android_common").Rule("combineJar").Inputs.Strings()
-		depPath := filepath.Join("out", "soong", ".intermediates", dep, "android_common", "turbine-combined", dep+".jar")
-		android.AssertStringListContainsEquals(t, "bad combined inputs for "+sdklib, combineJarInputs, depPath, combined)
-	}
-	for _, expectation := range expectations {
-		verify("sdklib", expectation.lib, expectation.on_impl_classpath, expectation.in_impl_combined)
-		verify("sdklib.impl", expectation.lib, expectation.on_impl_classpath, expectation.in_impl_combined)
-
-		stubName := apiScopePublic.stubsLibraryModuleName("sdklib")
-		verify(stubName, expectation.lib, expectation.on_stub_classpath, expectation.in_stub_combined)
-	}
-}
-
-func TestJavaSdkLibrary_DoNotAccessImplWhenItIsNotBuilt(t *testing.T) {
-	result := android.GroupFixturePreparers(
-		prepareForJavaTest,
-		PrepareForTestWithJavaSdkLibraryFiles,
-		FixtureWithLastReleaseApis("foo"),
-	).RunTestWithBp(t, `
-		java_sdk_library {
-			name: "foo",
-			srcs: ["a.java"],
-			api_only: true,
-			public: {
-				enabled: true,
-			},
-		}
-
-		java_library {
-			name: "bar",
-			srcs: ["b.java"],
-			libs: ["foo"],
-		}
-		`)
-
-	// The bar library should depend on the stubs jar.
-	barLibrary := result.ModuleForTests("bar", "android_common").Rule("javac")
-	if expected, actual := `^-classpath .*:out/soong/[^:]*/turbine-combined/foo\.stubs\.jar$`, barLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
-		t.Errorf("expected %q, found %#q", expected, actual)
-	}
-}
-
-func TestJavaSdkLibrary_UseSourcesFromAnotherSdkLibrary(t *testing.T) {
-	android.GroupFixturePreparers(
-		prepareForJavaTest,
-		PrepareForTestWithJavaSdkLibraryFiles,
-		FixtureWithLastReleaseApis("foo"),
-	).RunTestWithBp(t, `
-		java_sdk_library {
-			name: "foo",
-			srcs: ["a.java"],
-			api_packages: ["foo"],
-			public: {
-				enabled: true,
-			},
-		}
-
-		java_library {
-			name: "bar",
-			srcs: ["b.java", ":foo{.public.stubs.source}"],
-		}
-		`)
-}
-
-func TestJavaSdkLibrary_AccessOutputFiles_MissingScope(t *testing.T) {
-	android.GroupFixturePreparers(
-		prepareForJavaTest,
-		PrepareForTestWithJavaSdkLibraryFiles,
-		FixtureWithLastReleaseApis("foo"),
-	).
-		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`"foo" does not provide api scope system`)).
-		RunTestWithBp(t, `
-		java_sdk_library {
-			name: "foo",
-			srcs: ["a.java"],
-			api_packages: ["foo"],
-			public: {
-				enabled: true,
-			},
-		}
-
-		java_library {
-			name: "bar",
-			srcs: ["b.java", ":foo{.system.stubs.source}"],
-		}
-		`)
-}
-
-func TestJavaSdkLibrary_Deps(t *testing.T) {
-	result := android.GroupFixturePreparers(
-		prepareForJavaTest,
-		PrepareForTestWithJavaSdkLibraryFiles,
-		FixtureWithLastReleaseApis("sdklib"),
-	).RunTestWithBp(t, `
-		java_sdk_library {
-			name: "sdklib",
-			srcs: ["a.java"],
-			sdk_version: "none",
-			system_modules: "none",
-			public: {
-				enabled: true,
-			},
-		}
-		`)
-
-	CheckModuleDependencies(t, result.TestContext, "sdklib", "android_common", []string{
-		`dex2oatd`,
-		`sdklib.impl`,
-		`sdklib.stubs`,
-		`sdklib.stubs.source`,
-		`sdklib.xml`,
-	})
-}
-
-func TestJavaSdkLibraryImport_AccessOutputFiles(t *testing.T) {
-	prepareForJavaTest.RunTestWithBp(t, `
-		java_sdk_library_import {
-			name: "foo",
-			public: {
-				jars: ["a.jar"],
-				stub_srcs: ["a.java"],
-				current_api: "api/current.txt",
-				removed_api: "api/removed.txt",
-			},
-		}
-
-		java_library {
-			name: "bar",
-			srcs: [":foo{.public.stubs.source}"],
-			java_resources: [
-				":foo{.public.api.txt}",
-				":foo{.public.removed-api.txt}",
-			],
-		}
-		`)
-}
-
-func TestJavaSdkLibraryImport_AccessOutputFiles_Invalid(t *testing.T) {
-	bp := `
-		java_sdk_library_import {
-			name: "foo",
-			public: {
-				jars: ["a.jar"],
-			},
-		}
-		`
-
-	t.Run("stubs.source", func(t *testing.T) {
-		prepareForJavaTest.
-			ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`stubs.source not available for api scope public`)).
-			RunTestWithBp(t, bp+`
-				java_library {
-					name: "bar",
-					srcs: [":foo{.public.stubs.source}"],
-					java_resources: [
-						":foo{.public.api.txt}",
-						":foo{.public.removed-api.txt}",
-					],
-				}
-			`)
-	})
-
-	t.Run("api.txt", func(t *testing.T) {
-		prepareForJavaTest.
-			ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`api.txt not available for api scope public`)).
-			RunTestWithBp(t, bp+`
-				java_library {
-					name: "bar",
-					srcs: ["a.java"],
-					java_resources: [
-						":foo{.public.api.txt}",
-					],
-				}
-			`)
-	})
-
-	t.Run("removed-api.txt", func(t *testing.T) {
-		prepareForJavaTest.
-			ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`removed-api.txt not available for api scope public`)).
-			RunTestWithBp(t, bp+`
-				java_library {
-					name: "bar",
-					srcs: ["a.java"],
-					java_resources: [
-						":foo{.public.removed-api.txt}",
-					],
-				}
-			`)
-	})
-}
-
-func TestJavaSdkLibrary_InvalidScopes(t *testing.T) {
-	prepareForJavaTest.
-		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module "foo": enabled api scope "system" depends on disabled scope "public"`)).
-		RunTestWithBp(t, `
-			java_sdk_library {
-				name: "foo",
-				srcs: ["a.java", "b.java"],
-				api_packages: ["foo"],
-				// Explicitly disable public to test the check that ensures the set of enabled
-				// scopes is consistent.
-				public: {
-					enabled: false,
-				},
-				system: {
-					enabled: true,
-				},
-			}
-		`)
-}
-
-func TestJavaSdkLibrary_SdkVersion_ForScope(t *testing.T) {
-	android.GroupFixturePreparers(
-		prepareForJavaTest,
-		PrepareForTestWithJavaSdkLibraryFiles,
-		FixtureWithLastReleaseApis("foo"),
-	).RunTestWithBp(t, `
-		java_sdk_library {
-			name: "foo",
-			srcs: ["a.java", "b.java"],
-			api_packages: ["foo"],
-			system: {
-				enabled: true,
-				sdk_version: "module_current",
-			},
-		}
-		`)
-}
-
-func TestJavaSdkLibrary_ModuleLib(t *testing.T) {
-	android.GroupFixturePreparers(
-		prepareForJavaTest,
-		PrepareForTestWithJavaSdkLibraryFiles,
-		FixtureWithLastReleaseApis("foo"),
-	).RunTestWithBp(t, `
-		java_sdk_library {
-			name: "foo",
-			srcs: ["a.java", "b.java"],
-			api_packages: ["foo"],
-			system: {
-				enabled: true,
-			},
-			module_lib: {
-				enabled: true,
-			},
-		}
-		`)
-}
-
-func TestJavaSdkLibrary_SystemServer(t *testing.T) {
-	android.GroupFixturePreparers(
-		prepareForJavaTest,
-		PrepareForTestWithJavaSdkLibraryFiles,
-		FixtureWithLastReleaseApis("foo"),
-	).RunTestWithBp(t, `
-		java_sdk_library {
-			name: "foo",
-			srcs: ["a.java", "b.java"],
-			api_packages: ["foo"],
-			system: {
-				enabled: true,
-			},
-			system_server: {
-				enabled: true,
-			},
-		}
-		`)
-}
-
-func TestJavaSdkLibrary_MissingScope(t *testing.T) {
-	prepareForJavaTest.
-		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`requires api scope module-lib from foo but it only has \[\] available`)).
-		RunTestWithBp(t, `
-			java_sdk_library {
-				name: "foo",
-				srcs: ["a.java"],
-				public: {
-					enabled: false,
-				},
-			}
-
-			java_library {
-				name: "baz",
-				srcs: ["a.java"],
-				libs: ["foo"],
-				sdk_version: "module_current",
-			}
-		`)
-}
-
-func TestJavaSdkLibrary_FallbackScope(t *testing.T) {
-	android.GroupFixturePreparers(
-		prepareForJavaTest,
-		PrepareForTestWithJavaSdkLibraryFiles,
-		FixtureWithLastReleaseApis("foo"),
-	).RunTestWithBp(t, `
-		java_sdk_library {
-			name: "foo",
-			srcs: ["a.java"],
-			system: {
-				enabled: true,
-			},
-		}
-
-		java_library {
-			name: "baz",
-			srcs: ["a.java"],
-			libs: ["foo"],
-			// foo does not have module-lib scope so it should fallback to system
-			sdk_version: "module_current",
-		}
-		`)
-}
-
-func TestJavaSdkLibrary_DefaultToStubs(t *testing.T) {
-	result := android.GroupFixturePreparers(
-		prepareForJavaTest,
-		PrepareForTestWithJavaSdkLibraryFiles,
-		FixtureWithLastReleaseApis("foo"),
-	).RunTestWithBp(t, `
-		java_sdk_library {
-			name: "foo",
-			srcs: ["a.java"],
-			system: {
-				enabled: true,
-			},
-			default_to_stubs: true,
-		}
-
-		java_library {
-			name: "baz",
-			srcs: ["a.java"],
-			libs: ["foo"],
-			// does not have sdk_version set, should fallback to module,
-			// which will then fallback to system because the module scope
-			// is not enabled.
-		}
-		`)
-	// The baz library should depend on the system stubs jar.
-	bazLibrary := result.ModuleForTests("baz", "android_common").Rule("javac")
-	if expected, actual := `^-classpath .*:out/soong/[^:]*/turbine-combined/foo\.stubs.system\.jar$`, bazLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
-		t.Errorf("expected %q, found %#q", expected, actual)
-	}
-}
-
 var compilerFlagsTestCases = []struct {
 	in  string
 	out bool
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 8f36758..ecf2b1a 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -457,6 +457,11 @@
 	// * API incompatibilities baseline filegroup -> <dist-stem>-incompatibilities.api.<scope>.latest
 	Dist_stem *string
 
+	// The subdirectory for the artifacts that are copied to the dist directory.  If not specified
+	// then defaults to "android".  Should be set to "android" for anything that should be published
+	// in the public Android SDK.
+	Dist_group *string
+
 	// A compatibility mode that allows historical API-tracking files to not exist.
 	// Do not use.
 	Unsafe_ignore_missing_latest_api bool
@@ -1198,12 +1203,10 @@
 
 // The dist path of the stub artifacts
 func (module *SdkLibrary) apiDistPath(apiScope *apiScope) string {
-	if module.ModuleBase.Owner() != "" {
-		return path.Join("apistubs", module.ModuleBase.Owner(), apiScope.name)
-	} else if Bool(module.sdkLibraryProperties.Core_lib) {
+	if Bool(module.sdkLibraryProperties.Core_lib) {
 		return path.Join("apistubs", "core", apiScope.name)
 	} else {
-		return path.Join("apistubs", "android", apiScope.name)
+		return path.Join("apistubs", module.distGroup(), apiScope.name)
 	}
 }
 
@@ -1228,6 +1231,19 @@
 	return proptools.StringDefault(module.sdkLibraryProperties.Dist_stem, module.BaseModuleName())
 }
 
+// distGroup returns the subdirectory of the dist path of the stub artifacts.
+func (module *SdkLibrary) distGroup() string {
+	if group := proptools.String(module.sdkLibraryProperties.Dist_group); group != "" {
+		return group
+	}
+	// TODO(b/186723288): Remove this once everything uses dist_group.
+	if owner := module.ModuleBase.Owner(); owner != "" {
+		return owner
+	}
+	// TODO(b/186723288): Make this "unknown".
+	return "android"
+}
+
 func (module *SdkLibrary) latestApiFilegroupName(apiScope *apiScope) string {
 	return ":" + module.distStem() + ".api." + apiScope.name + ".latest"
 }
diff --git a/java/sdk_library_test.go b/java/sdk_library_test.go
new file mode 100644
index 0000000..0fe6e72
--- /dev/null
+++ b/java/sdk_library_test.go
@@ -0,0 +1,931 @@
+// 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 java
+
+import (
+	"android/soong/android"
+	"fmt"
+	"path/filepath"
+	"regexp"
+	"testing"
+
+	"github.com/google/blueprint/proptools"
+)
+
+func TestJavaSdkLibrary(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		prepareForJavaTest,
+		PrepareForTestWithJavaSdkLibraryFiles,
+		FixtureWithPrebuiltApis(map[string][]string{
+			"28": {"foo"},
+			"29": {"foo"},
+			"30": {"bar", "barney", "baz", "betty", "foo", "fred", "quuz", "wilma"},
+		}),
+	).RunTestWithBp(t, `
+		droiddoc_exported_dir {
+			name: "droiddoc-templates-sdk",
+			path: ".",
+		}
+		java_sdk_library {
+			name: "foo",
+			srcs: ["a.java", "b.java"],
+			api_packages: ["foo"],
+		}
+		java_sdk_library {
+			name: "bar",
+			srcs: ["a.java", "b.java"],
+			api_packages: ["bar"],
+		}
+		java_library {
+			name: "baz",
+			srcs: ["c.java"],
+			libs: ["foo", "bar.stubs"],
+			sdk_version: "system_current",
+		}
+		java_sdk_library {
+			name: "barney",
+			srcs: ["c.java"],
+			api_only: true,
+		}
+		java_sdk_library {
+			name: "betty",
+			srcs: ["c.java"],
+			shared_library: false,
+		}
+		java_sdk_library_import {
+		    name: "quuz",
+				public: {
+					jars: ["c.jar"],
+				},
+		}
+		java_sdk_library_import {
+		    name: "fred",
+				public: {
+					jars: ["b.jar"],
+				},
+		}
+		java_sdk_library_import {
+		    name: "wilma",
+				public: {
+					jars: ["b.jar"],
+				},
+				shared_library: false,
+		}
+		java_library {
+		    name: "qux",
+		    srcs: ["c.java"],
+		    libs: ["baz", "fred", "quuz.stubs", "wilma", "barney", "betty"],
+		    sdk_version: "system_current",
+		}
+		java_library {
+			name: "baz-test",
+			srcs: ["c.java"],
+			libs: ["foo"],
+			sdk_version: "test_current",
+		}
+		java_library {
+			name: "baz-29",
+			srcs: ["c.java"],
+			libs: ["foo"],
+			sdk_version: "system_29",
+		}
+		java_library {
+			name: "baz-module-30",
+			srcs: ["c.java"],
+			libs: ["foo"],
+			sdk_version: "module_30",
+		}
+		`)
+
+	// check the existence of the internal modules
+	result.ModuleForTests("foo", "android_common")
+	result.ModuleForTests(apiScopePublic.stubsLibraryModuleName("foo"), "android_common")
+	result.ModuleForTests(apiScopeSystem.stubsLibraryModuleName("foo"), "android_common")
+	result.ModuleForTests(apiScopeTest.stubsLibraryModuleName("foo"), "android_common")
+	result.ModuleForTests(apiScopePublic.stubsSourceModuleName("foo"), "android_common")
+	result.ModuleForTests(apiScopeSystem.stubsSourceModuleName("foo"), "android_common")
+	result.ModuleForTests(apiScopeTest.stubsSourceModuleName("foo"), "android_common")
+	result.ModuleForTests("foo"+sdkXmlFileSuffix, "android_common")
+	result.ModuleForTests("foo.api.public.28", "")
+	result.ModuleForTests("foo.api.system.28", "")
+	result.ModuleForTests("foo.api.test.28", "")
+
+	bazJavac := result.ModuleForTests("baz", "android_common").Rule("javac")
+	// tests if baz is actually linked to the stubs lib
+	android.AssertStringDoesContain(t, "baz javac classpath", bazJavac.Args["classpath"], "foo.stubs.system.jar")
+	// ... and not to the impl lib
+	android.AssertStringDoesNotContain(t, "baz javac classpath", bazJavac.Args["classpath"], "foo.jar")
+	// test if baz is not linked to the system variant of foo
+	android.AssertStringDoesNotContain(t, "baz javac classpath", bazJavac.Args["classpath"], "foo.stubs.jar")
+
+	bazTestJavac := result.ModuleForTests("baz-test", "android_common").Rule("javac")
+	// tests if baz-test is actually linked to the test stubs lib
+	android.AssertStringDoesContain(t, "baz-test javac classpath", bazTestJavac.Args["classpath"], "foo.stubs.test.jar")
+
+	baz29Javac := result.ModuleForTests("baz-29", "android_common").Rule("javac")
+	// tests if baz-29 is actually linked to the system 29 stubs lib
+	android.AssertStringDoesContain(t, "baz-29 javac classpath", baz29Javac.Args["classpath"], "prebuilts/sdk/29/system/foo.jar")
+
+	bazModule30Javac := result.ModuleForTests("baz-module-30", "android_common").Rule("javac")
+	// tests if "baz-module-30" is actually linked to the module 30 stubs lib
+	android.AssertStringDoesContain(t, "baz-module-30 javac classpath", bazModule30Javac.Args["classpath"], "prebuilts/sdk/30/module-lib/foo.jar")
+
+	// test if baz has exported SDK lib names foo and bar to qux
+	qux := result.ModuleForTests("qux", "android_common")
+	if quxLib, ok := qux.Module().(*Library); ok {
+		sdkLibs := quxLib.ClassLoaderContexts().UsesLibs()
+		android.AssertDeepEquals(t, "qux exports", []string{"foo", "bar", "fred", "quuz"}, sdkLibs)
+	}
+}
+
+func TestJavaSdkLibrary_StubOrImplOnlyLibs(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		prepareForJavaTest,
+		PrepareForTestWithJavaSdkLibraryFiles,
+		FixtureWithLastReleaseApis("sdklib"),
+	).RunTestWithBp(t, `
+		java_sdk_library {
+			name: "sdklib",
+			srcs: ["a.java"],
+			libs: ["lib"],
+			static_libs: ["static-lib"],
+			impl_only_libs: ["impl-only-lib"],
+			stub_only_libs: ["stub-only-lib"],
+			stub_only_static_libs: ["stub-only-static-lib"],
+		}
+		java_defaults {
+			name: "defaults",
+			srcs: ["a.java"],
+			sdk_version: "current",
+		}
+		java_library { name: "lib", defaults: ["defaults"] }
+		java_library { name: "static-lib", defaults: ["defaults"] }
+		java_library { name: "impl-only-lib", defaults: ["defaults"] }
+		java_library { name: "stub-only-lib", defaults: ["defaults"] }
+		java_library { name: "stub-only-static-lib", defaults: ["defaults"] }
+		`)
+	var expectations = []struct {
+		lib               string
+		on_impl_classpath bool
+		on_stub_classpath bool
+		in_impl_combined  bool
+		in_stub_combined  bool
+	}{
+		{lib: "lib", on_impl_classpath: true},
+		{lib: "static-lib", in_impl_combined: true},
+		{lib: "impl-only-lib", on_impl_classpath: true},
+		{lib: "stub-only-lib", on_stub_classpath: true},
+		{lib: "stub-only-static-lib", in_stub_combined: true},
+	}
+	verify := func(sdklib, dep string, cp, combined bool) {
+		sdklibCp := result.ModuleForTests(sdklib, "android_common").Rule("javac").Args["classpath"]
+		expected := cp || combined // Every combined jar is also on the classpath.
+		android.AssertStringContainsEquals(t, "bad classpath for "+sdklib, sdklibCp, "/"+dep+".jar", expected)
+
+		combineJarInputs := result.ModuleForTests(sdklib, "android_common").Rule("combineJar").Inputs.Strings()
+		depPath := filepath.Join("out", "soong", ".intermediates", dep, "android_common", "turbine-combined", dep+".jar")
+		android.AssertStringListContainsEquals(t, "bad combined inputs for "+sdklib, combineJarInputs, depPath, combined)
+	}
+	for _, expectation := range expectations {
+		verify("sdklib", expectation.lib, expectation.on_impl_classpath, expectation.in_impl_combined)
+		verify("sdklib.impl", expectation.lib, expectation.on_impl_classpath, expectation.in_impl_combined)
+
+		stubName := apiScopePublic.stubsLibraryModuleName("sdklib")
+		verify(stubName, expectation.lib, expectation.on_stub_classpath, expectation.in_stub_combined)
+	}
+}
+
+func TestJavaSdkLibrary_DoNotAccessImplWhenItIsNotBuilt(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		prepareForJavaTest,
+		PrepareForTestWithJavaSdkLibraryFiles,
+		FixtureWithLastReleaseApis("foo"),
+	).RunTestWithBp(t, `
+		java_sdk_library {
+			name: "foo",
+			srcs: ["a.java"],
+			api_only: true,
+			public: {
+				enabled: true,
+			},
+		}
+
+		java_library {
+			name: "bar",
+			srcs: ["b.java"],
+			libs: ["foo"],
+		}
+		`)
+
+	// The bar library should depend on the stubs jar.
+	barLibrary := result.ModuleForTests("bar", "android_common").Rule("javac")
+	if expected, actual := `^-classpath .*:out/soong/[^:]*/turbine-combined/foo\.stubs\.jar$`, barLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
+		t.Errorf("expected %q, found %#q", expected, actual)
+	}
+}
+
+func TestJavaSdkLibrary_UseSourcesFromAnotherSdkLibrary(t *testing.T) {
+	android.GroupFixturePreparers(
+		prepareForJavaTest,
+		PrepareForTestWithJavaSdkLibraryFiles,
+		FixtureWithLastReleaseApis("foo"),
+	).RunTestWithBp(t, `
+		java_sdk_library {
+			name: "foo",
+			srcs: ["a.java"],
+			api_packages: ["foo"],
+			public: {
+				enabled: true,
+			},
+		}
+
+		java_library {
+			name: "bar",
+			srcs: ["b.java", ":foo{.public.stubs.source}"],
+		}
+		`)
+}
+
+func TestJavaSdkLibrary_AccessOutputFiles_MissingScope(t *testing.T) {
+	android.GroupFixturePreparers(
+		prepareForJavaTest,
+		PrepareForTestWithJavaSdkLibraryFiles,
+		FixtureWithLastReleaseApis("foo"),
+	).
+		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`"foo" does not provide api scope system`)).
+		RunTestWithBp(t, `
+		java_sdk_library {
+			name: "foo",
+			srcs: ["a.java"],
+			api_packages: ["foo"],
+			public: {
+				enabled: true,
+			},
+		}
+
+		java_library {
+			name: "bar",
+			srcs: ["b.java", ":foo{.system.stubs.source}"],
+		}
+		`)
+}
+
+func TestJavaSdkLibrary_Deps(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		prepareForJavaTest,
+		PrepareForTestWithJavaSdkLibraryFiles,
+		FixtureWithLastReleaseApis("sdklib"),
+	).RunTestWithBp(t, `
+		java_sdk_library {
+			name: "sdklib",
+			srcs: ["a.java"],
+			sdk_version: "none",
+			system_modules: "none",
+			public: {
+				enabled: true,
+			},
+		}
+		`)
+
+	CheckModuleDependencies(t, result.TestContext, "sdklib", "android_common", []string{
+		`dex2oatd`,
+		`sdklib.impl`,
+		`sdklib.stubs`,
+		`sdklib.stubs.source`,
+		`sdklib.xml`,
+	})
+}
+
+func TestJavaSdkLibraryImport_AccessOutputFiles(t *testing.T) {
+	prepareForJavaTest.RunTestWithBp(t, `
+		java_sdk_library_import {
+			name: "foo",
+			public: {
+				jars: ["a.jar"],
+				stub_srcs: ["a.java"],
+				current_api: "api/current.txt",
+				removed_api: "api/removed.txt",
+			},
+		}
+
+		java_library {
+			name: "bar",
+			srcs: [":foo{.public.stubs.source}"],
+			java_resources: [
+				":foo{.public.api.txt}",
+				":foo{.public.removed-api.txt}",
+			],
+		}
+		`)
+}
+
+func TestJavaSdkLibraryImport_AccessOutputFiles_Invalid(t *testing.T) {
+	bp := `
+		java_sdk_library_import {
+			name: "foo",
+			public: {
+				jars: ["a.jar"],
+			},
+		}
+		`
+
+	t.Run("stubs.source", func(t *testing.T) {
+		prepareForJavaTest.
+			ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`stubs.source not available for api scope public`)).
+			RunTestWithBp(t, bp+`
+				java_library {
+					name: "bar",
+					srcs: [":foo{.public.stubs.source}"],
+					java_resources: [
+						":foo{.public.api.txt}",
+						":foo{.public.removed-api.txt}",
+					],
+				}
+			`)
+	})
+
+	t.Run("api.txt", func(t *testing.T) {
+		prepareForJavaTest.
+			ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`api.txt not available for api scope public`)).
+			RunTestWithBp(t, bp+`
+				java_library {
+					name: "bar",
+					srcs: ["a.java"],
+					java_resources: [
+						":foo{.public.api.txt}",
+					],
+				}
+			`)
+	})
+
+	t.Run("removed-api.txt", func(t *testing.T) {
+		prepareForJavaTest.
+			ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`removed-api.txt not available for api scope public`)).
+			RunTestWithBp(t, bp+`
+				java_library {
+					name: "bar",
+					srcs: ["a.java"],
+					java_resources: [
+						":foo{.public.removed-api.txt}",
+					],
+				}
+			`)
+	})
+}
+
+func TestJavaSdkLibrary_InvalidScopes(t *testing.T) {
+	prepareForJavaTest.
+		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module "foo": enabled api scope "system" depends on disabled scope "public"`)).
+		RunTestWithBp(t, `
+			java_sdk_library {
+				name: "foo",
+				srcs: ["a.java", "b.java"],
+				api_packages: ["foo"],
+				// Explicitly disable public to test the check that ensures the set of enabled
+				// scopes is consistent.
+				public: {
+					enabled: false,
+				},
+				system: {
+					enabled: true,
+				},
+			}
+		`)
+}
+
+func TestJavaSdkLibrary_SdkVersion_ForScope(t *testing.T) {
+	android.GroupFixturePreparers(
+		prepareForJavaTest,
+		PrepareForTestWithJavaSdkLibraryFiles,
+		FixtureWithLastReleaseApis("foo"),
+	).RunTestWithBp(t, `
+		java_sdk_library {
+			name: "foo",
+			srcs: ["a.java", "b.java"],
+			api_packages: ["foo"],
+			system: {
+				enabled: true,
+				sdk_version: "module_current",
+			},
+		}
+		`)
+}
+
+func TestJavaSdkLibrary_ModuleLib(t *testing.T) {
+	android.GroupFixturePreparers(
+		prepareForJavaTest,
+		PrepareForTestWithJavaSdkLibraryFiles,
+		FixtureWithLastReleaseApis("foo"),
+	).RunTestWithBp(t, `
+		java_sdk_library {
+			name: "foo",
+			srcs: ["a.java", "b.java"],
+			api_packages: ["foo"],
+			system: {
+				enabled: true,
+			},
+			module_lib: {
+				enabled: true,
+			},
+		}
+		`)
+}
+
+func TestJavaSdkLibrary_SystemServer(t *testing.T) {
+	android.GroupFixturePreparers(
+		prepareForJavaTest,
+		PrepareForTestWithJavaSdkLibraryFiles,
+		FixtureWithLastReleaseApis("foo"),
+	).RunTestWithBp(t, `
+		java_sdk_library {
+			name: "foo",
+			srcs: ["a.java", "b.java"],
+			api_packages: ["foo"],
+			system: {
+				enabled: true,
+			},
+			system_server: {
+				enabled: true,
+			},
+		}
+		`)
+}
+
+func TestJavaSdkLibrary_MissingScope(t *testing.T) {
+	prepareForJavaTest.
+		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`requires api scope module-lib from foo but it only has \[\] available`)).
+		RunTestWithBp(t, `
+			java_sdk_library {
+				name: "foo",
+				srcs: ["a.java"],
+				public: {
+					enabled: false,
+				},
+			}
+
+			java_library {
+				name: "baz",
+				srcs: ["a.java"],
+				libs: ["foo"],
+				sdk_version: "module_current",
+			}
+		`)
+}
+
+func TestJavaSdkLibrary_FallbackScope(t *testing.T) {
+	android.GroupFixturePreparers(
+		prepareForJavaTest,
+		PrepareForTestWithJavaSdkLibraryFiles,
+		FixtureWithLastReleaseApis("foo"),
+	).RunTestWithBp(t, `
+		java_sdk_library {
+			name: "foo",
+			srcs: ["a.java"],
+			system: {
+				enabled: true,
+			},
+		}
+
+		java_library {
+			name: "baz",
+			srcs: ["a.java"],
+			libs: ["foo"],
+			// foo does not have module-lib scope so it should fallback to system
+			sdk_version: "module_current",
+		}
+		`)
+}
+
+func TestJavaSdkLibrary_DefaultToStubs(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		prepareForJavaTest,
+		PrepareForTestWithJavaSdkLibraryFiles,
+		FixtureWithLastReleaseApis("foo"),
+	).RunTestWithBp(t, `
+		java_sdk_library {
+			name: "foo",
+			srcs: ["a.java"],
+			system: {
+				enabled: true,
+			},
+			default_to_stubs: true,
+		}
+
+		java_library {
+			name: "baz",
+			srcs: ["a.java"],
+			libs: ["foo"],
+			// does not have sdk_version set, should fallback to module,
+			// which will then fallback to system because the module scope
+			// is not enabled.
+		}
+		`)
+	// The baz library should depend on the system stubs jar.
+	bazLibrary := result.ModuleForTests("baz", "android_common").Rule("javac")
+	if expected, actual := `^-classpath .*:out/soong/[^:]*/turbine-combined/foo\.stubs.system\.jar$`, bazLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
+		t.Errorf("expected %q, found %#q", expected, actual)
+	}
+}
+
+func TestJavaSdkLibraryImport(t *testing.T) {
+	result := prepareForJavaTest.RunTestWithBp(t, `
+		java_library {
+			name: "foo",
+			srcs: ["a.java"],
+			libs: ["sdklib"],
+			sdk_version: "current",
+		}
+
+		java_library {
+			name: "foo.system",
+			srcs: ["a.java"],
+			libs: ["sdklib"],
+			sdk_version: "system_current",
+		}
+
+		java_library {
+			name: "foo.test",
+			srcs: ["a.java"],
+			libs: ["sdklib"],
+			sdk_version: "test_current",
+		}
+
+		java_sdk_library_import {
+			name: "sdklib",
+			public: {
+				jars: ["a.jar"],
+			},
+			system: {
+				jars: ["b.jar"],
+			},
+			test: {
+				jars: ["c.jar"],
+				stub_srcs: ["c.java"],
+			},
+		}
+		`)
+
+	for _, scope := range []string{"", ".system", ".test"} {
+		fooModule := result.ModuleForTests("foo"+scope, "android_common")
+		javac := fooModule.Rule("javac")
+
+		sdklibStubsJar := result.ModuleForTests("sdklib.stubs"+scope, "android_common").Rule("combineJar").Output
+		android.AssertStringDoesContain(t, "foo classpath", javac.Args["classpath"], sdklibStubsJar.String())
+	}
+
+	CheckModuleDependencies(t, result.TestContext, "sdklib", "android_common", []string{
+		`prebuilt_sdklib.stubs`,
+		`prebuilt_sdklib.stubs.source.test`,
+		`prebuilt_sdklib.stubs.system`,
+		`prebuilt_sdklib.stubs.test`,
+	})
+}
+
+func TestJavaSdkLibraryImport_WithSource(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		prepareForJavaTest,
+		PrepareForTestWithJavaSdkLibraryFiles,
+		FixtureWithLastReleaseApis("sdklib"),
+	).RunTestWithBp(t, `
+		java_sdk_library {
+			name: "sdklib",
+			srcs: ["a.java"],
+			sdk_version: "none",
+			system_modules: "none",
+			public: {
+				enabled: true,
+			},
+		}
+
+		java_sdk_library_import {
+			name: "sdklib",
+			public: {
+				jars: ["a.jar"],
+			},
+		}
+		`)
+
+	CheckModuleDependencies(t, result.TestContext, "sdklib", "android_common", []string{
+		`dex2oatd`,
+		`prebuilt_sdklib`,
+		`sdklib.impl`,
+		`sdklib.stubs`,
+		`sdklib.stubs.source`,
+		`sdklib.xml`,
+	})
+
+	CheckModuleDependencies(t, result.TestContext, "prebuilt_sdklib", "android_common", []string{
+		`prebuilt_sdklib.stubs`,
+		`sdklib.impl`,
+		// This should be prebuilt_sdklib.stubs but is set to sdklib.stubs because the
+		// dependency is added after prebuilts may have been renamed and so has to use
+		// the renamed name.
+		`sdklib.xml`,
+	})
+}
+
+func TestJavaSdkLibraryImport_Preferred(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		prepareForJavaTest,
+		PrepareForTestWithJavaSdkLibraryFiles,
+		FixtureWithLastReleaseApis("sdklib"),
+	).RunTestWithBp(t, `
+		java_sdk_library {
+			name: "sdklib",
+			srcs: ["a.java"],
+			sdk_version: "none",
+			system_modules: "none",
+			public: {
+				enabled: true,
+			},
+		}
+
+		java_sdk_library_import {
+			name: "sdklib",
+			prefer: true,
+			public: {
+				jars: ["a.jar"],
+			},
+		}
+		`)
+
+	CheckModuleDependencies(t, result.TestContext, "sdklib", "android_common", []string{
+		`dex2oatd`,
+		`prebuilt_sdklib`,
+		`sdklib.impl`,
+		`sdklib.stubs`,
+		`sdklib.stubs.source`,
+		`sdklib.xml`,
+	})
+
+	CheckModuleDependencies(t, result.TestContext, "prebuilt_sdklib", "android_common", []string{
+		`prebuilt_sdklib.stubs`,
+		`sdklib.impl`,
+		`sdklib.xml`,
+	})
+}
+
+func TestJavaSdkLibraryEnforce(t *testing.T) {
+	partitionToBpOption := func(partition string) string {
+		switch partition {
+		case "system":
+			return ""
+		case "vendor":
+			return "soc_specific: true,"
+		case "product":
+			return "product_specific: true,"
+		default:
+			panic("Invalid partition group name: " + partition)
+		}
+	}
+
+	type testConfigInfo struct {
+		libraryType                string
+		fromPartition              string
+		toPartition                string
+		enforceVendorInterface     bool
+		enforceProductInterface    bool
+		enforceJavaSdkLibraryCheck bool
+		allowList                  []string
+	}
+
+	createPreparer := func(info testConfigInfo) android.FixturePreparer {
+		bpFileTemplate := `
+			java_library {
+				name: "foo",
+				srcs: ["foo.java"],
+				libs: ["bar"],
+				sdk_version: "current",
+				%s
+			}
+
+			%s {
+				name: "bar",
+				srcs: ["bar.java"],
+				sdk_version: "current",
+				%s
+			}
+		`
+
+		bpFile := fmt.Sprintf(bpFileTemplate,
+			partitionToBpOption(info.fromPartition),
+			info.libraryType,
+			partitionToBpOption(info.toPartition))
+
+		return android.GroupFixturePreparers(
+			PrepareForTestWithJavaSdkLibraryFiles,
+			FixtureWithLastReleaseApis("bar"),
+			android.FixtureWithRootAndroidBp(bpFile),
+			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+				variables.EnforceProductPartitionInterface = proptools.BoolPtr(info.enforceProductInterface)
+				if info.enforceVendorInterface {
+					variables.DeviceVndkVersion = proptools.StringPtr("current")
+				}
+				variables.EnforceInterPartitionJavaSdkLibrary = proptools.BoolPtr(info.enforceJavaSdkLibraryCheck)
+				variables.InterPartitionJavaLibraryAllowList = info.allowList
+			}),
+		)
+	}
+
+	runTest := func(t *testing.T, info testConfigInfo, expectedErrorPattern string) {
+		t.Run(fmt.Sprintf("%v", info), func(t *testing.T) {
+			errorHandler := android.FixtureExpectsNoErrors
+			if expectedErrorPattern != "" {
+				errorHandler = android.FixtureExpectsAtLeastOneErrorMatchingPattern(expectedErrorPattern)
+			}
+			android.GroupFixturePreparers(
+				prepareForJavaTest,
+				createPreparer(info),
+			).
+				ExtendWithErrorHandler(errorHandler).
+				RunTest(t)
+		})
+	}
+
+	errorMessage := "is not allowed across the partitions"
+
+	runTest(t, testConfigInfo{
+		libraryType:                "java_library",
+		fromPartition:              "product",
+		toPartition:                "system",
+		enforceVendorInterface:     true,
+		enforceProductInterface:    true,
+		enforceJavaSdkLibraryCheck: false,
+	}, "")
+
+	runTest(t, testConfigInfo{
+		libraryType:                "java_library",
+		fromPartition:              "product",
+		toPartition:                "system",
+		enforceVendorInterface:     true,
+		enforceProductInterface:    false,
+		enforceJavaSdkLibraryCheck: true,
+	}, "")
+
+	runTest(t, testConfigInfo{
+		libraryType:                "java_library",
+		fromPartition:              "product",
+		toPartition:                "system",
+		enforceVendorInterface:     true,
+		enforceProductInterface:    true,
+		enforceJavaSdkLibraryCheck: true,
+	}, errorMessage)
+
+	runTest(t, testConfigInfo{
+		libraryType:                "java_library",
+		fromPartition:              "vendor",
+		toPartition:                "system",
+		enforceVendorInterface:     true,
+		enforceProductInterface:    true,
+		enforceJavaSdkLibraryCheck: true,
+	}, errorMessage)
+
+	runTest(t, testConfigInfo{
+		libraryType:                "java_library",
+		fromPartition:              "vendor",
+		toPartition:                "system",
+		enforceVendorInterface:     true,
+		enforceProductInterface:    true,
+		enforceJavaSdkLibraryCheck: true,
+		allowList:                  []string{"bar"},
+	}, "")
+
+	runTest(t, testConfigInfo{
+		libraryType:                "java_library",
+		fromPartition:              "vendor",
+		toPartition:                "product",
+		enforceVendorInterface:     true,
+		enforceProductInterface:    true,
+		enforceJavaSdkLibraryCheck: true,
+	}, errorMessage)
+
+	runTest(t, testConfigInfo{
+		libraryType:                "java_sdk_library",
+		fromPartition:              "product",
+		toPartition:                "system",
+		enforceVendorInterface:     true,
+		enforceProductInterface:    true,
+		enforceJavaSdkLibraryCheck: true,
+	}, "")
+
+	runTest(t, testConfigInfo{
+		libraryType:                "java_sdk_library",
+		fromPartition:              "vendor",
+		toPartition:                "system",
+		enforceVendorInterface:     true,
+		enforceProductInterface:    true,
+		enforceJavaSdkLibraryCheck: true,
+	}, "")
+
+	runTest(t, testConfigInfo{
+		libraryType:                "java_sdk_library",
+		fromPartition:              "vendor",
+		toPartition:                "product",
+		enforceVendorInterface:     true,
+		enforceProductInterface:    true,
+		enforceJavaSdkLibraryCheck: true,
+	}, "")
+}
+
+func TestJavaSdkLibraryDist(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		PrepareForTestWithJavaBuildComponents,
+		PrepareForTestWithJavaDefaultModules,
+		PrepareForTestWithJavaSdkLibraryFiles,
+	).RunTestWithBp(t, `
+		java_sdk_library {
+			name: "sdklib_no_owner",
+			unsafe_ignore_missing_latest_api: true,
+			srcs: ["foo.java"],
+		}
+
+		java_sdk_library {
+			name: "sdklib_group_foo",
+			unsafe_ignore_missing_latest_api: true,
+			srcs: ["foo.java"],
+			dist_group: "foo",
+		}
+
+		java_sdk_library {
+			name: "sdklib_owner_foo",
+			unsafe_ignore_missing_latest_api: true,
+			srcs: ["foo.java"],
+			owner: "foo",
+		}
+
+		java_sdk_library {
+			name: "sdklib_stem_foo",
+			unsafe_ignore_missing_latest_api: true,
+			srcs: ["foo.java"],
+			dist_stem: "foo",
+		}
+
+		java_sdk_library {
+			name: "sdklib_core_lib",
+			unsafe_ignore_missing_latest_api: true,
+			srcs: ["foo.java"],
+			core_lib: true,
+		}
+	`)
+
+	type testCase struct {
+		module   string
+		distDir  string
+		distStem string
+	}
+	testCases := []testCase{
+		{
+			module:   "sdklib_no_owner",
+			distDir:  "apistubs/android/public",
+			distStem: "sdklib_no_owner.jar",
+		},
+		{
+			module:   "sdklib_group_foo",
+			distDir:  "apistubs/foo/public",
+			distStem: "sdklib_group_foo.jar",
+		},
+		{
+			module:   "sdklib_owner_foo",
+			distDir:  "apistubs/foo/public",
+			distStem: "sdklib_owner_foo.jar",
+		},
+		{
+			module:   "sdklib_stem_foo",
+			distDir:  "apistubs/android/public",
+			distStem: "foo.jar",
+		},
+		{
+			module:   "sdklib_core_lib",
+			distDir:  "apistubs/core/public",
+			distStem: "sdklib_core_lib.jar",
+		},
+	}
+
+	for _, tt := range testCases {
+		t.Run(tt.module, func(t *testing.T) {
+			m := result.ModuleForTests(tt.module+".stubs", "android_common").Module().(*Library)
+			dists := m.Dists()
+			if len(dists) != 1 {
+				t.Fatalf("expected exactly 1 dist entry, got %d", len(dists))
+			}
+			if g, w := String(dists[0].Dir), tt.distDir; g != w {
+				t.Errorf("expected dist dir %q, got %q", w, g)
+			}
+			if g, w := String(dists[0].Dest), tt.distStem; g != w {
+				t.Errorf("expected dist stem %q, got %q", w, g)
+			}
+		})
+	}
+}
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/build-mainline-modules.sh b/scripts/build-mainline-modules.sh
index 30cb937..39c8fba 100755
--- a/scripts/build-mainline-modules.sh
+++ b/scripts/build-mainline-modules.sh
@@ -68,6 +68,9 @@
 # Make sure this build builds from source, regardless of the default.
 export SOONG_CONFIG_art_module_source_build=true
 
+# This script does not intend to handle compressed APEX
+export OVERRIDE_PRODUCT_COMPRESSED_APEX=false
+
 OUT_DIR=$(source build/envsetup.sh > /dev/null; TARGET_PRODUCT= get_build_var OUT_DIR)
 DIST_DIR=$(source build/envsetup.sh > /dev/null; TARGET_PRODUCT= get_build_var DIST_DIR)
 
diff --git a/tests/mixed_mode_test.sh b/tests/mixed_mode_test.sh
index 80774bf..b408fd3 100755
--- a/tests/mixed_mode_test.sh
+++ b/tests/mixed_mode_test.sh
@@ -14,7 +14,7 @@
   setup
   create_mock_bazel
 
-  run_bazel info
+  STANDALONE_BAZEL=true run_bazel info
 }
 
 test_bazel_smoke
diff --git a/ui/build/Android.bp b/ui/build/Android.bp
index d17b464..37940ba 100644
--- a/ui/build/Android.bp
+++ b/ui/build/Android.bp
@@ -60,6 +60,7 @@
         "path.go",
         "proc_sync.go",
         "rbe.go",
+        "sandbox_config.go",
         "signal.go",
         "soong.go",
         "test_build.go",
@@ -86,5 +87,8 @@
             "config_linux.go",
             "sandbox_linux.go",
         ],
+        testSrcs: [
+            "sandbox_linux_test.go",
+        ],
     },
 }
diff --git a/ui/build/config.go b/ui/build/config.go
index 3ebde0d..f1f5989 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -57,6 +57,7 @@
 	katiSuffix      string
 	targetDevice    string
 	targetDeviceDir string
+	sandboxConfig   *SandboxConfig
 
 	// Autodetected
 	totalRAM uint64
@@ -123,7 +124,8 @@
 
 func NewConfig(ctx Context, args ...string) Config {
 	ret := &configImpl{
-		environ: OsEnvironment(),
+		environ:       OsEnvironment(),
+		sandboxConfig: &SandboxConfig{},
 	}
 
 	// Default matching ninja
diff --git a/ui/build/dumpvars.go b/ui/build/dumpvars.go
index 54aeda0..83c8865 100644
--- a/ui/build/dumpvars.go
+++ b/ui/build/dumpvars.go
@@ -225,6 +225,10 @@
 		// Extra environment variables to be exported to ninja
 		"BUILD_BROKEN_NINJA_USES_ENV_VARS",
 
+		// Used to restrict write access to source tree
+		"BUILD_BROKEN_SRC_DIR_IS_WRITABLE",
+		"BUILD_BROKEN_SRC_DIR_RW_ALLOWLIST",
+
 		// Not used, but useful to be in the soong.log
 		"BOARD_VNDK_VERSION",
 
@@ -280,6 +284,8 @@
 	config.SetNinjaArgs(strings.Fields(makeVars["NINJA_GOALS"]))
 	config.SetTargetDevice(makeVars["TARGET_DEVICE"])
 	config.SetTargetDeviceDir(makeVars["TARGET_DEVICE_DIR"])
+	config.sandboxConfig.SetSrcDirIsRO(makeVars["BUILD_BROKEN_SRC_DIR_IS_WRITABLE"] == "false")
+	config.sandboxConfig.SetSrcDirRWAllowlist(strings.Fields(makeVars["BUILD_BROKEN_SRC_DIR_RW_ALLOWLIST"]))
 
 	config.SetBuildBrokenDupRules(makeVars["BUILD_BROKEN_DUP_RULES"] == "true")
 	config.SetBuildBrokenUsesNetwork(makeVars["BUILD_BROKEN_USES_NETWORK"] == "true")
diff --git a/ui/build/sandbox_config.go b/ui/build/sandbox_config.go
new file mode 100644
index 0000000..1b46459
--- /dev/null
+++ b/ui/build/sandbox_config.go
@@ -0,0 +1,36 @@
+// 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 build
+
+type SandboxConfig struct {
+	srcDirIsRO        bool
+	srcDirRWAllowlist []string
+}
+
+func (sc *SandboxConfig) SetSrcDirIsRO(ro bool) {
+	sc.srcDirIsRO = ro
+}
+
+func (sc *SandboxConfig) SrcDirIsRO() bool {
+	return sc.srcDirIsRO
+}
+
+func (sc *SandboxConfig) SetSrcDirRWAllowlist(allowlist []string) {
+	sc.srcDirRWAllowlist = allowlist
+}
+
+func (sc *SandboxConfig) SrcDirRWAllowlist() []string {
+	return sc.srcDirRWAllowlist
+}
diff --git a/ui/build/sandbox_linux.go b/ui/build/sandbox_linux.go
index dab0e75..b0a6748 100644
--- a/ui/build/sandbox_linux.go
+++ b/ui/build/sandbox_linux.go
@@ -145,6 +145,13 @@
 func (c *Cmd) wrapSandbox() {
 	wd, _ := os.Getwd()
 
+	var srcDirMountFlag string
+	if c.config.sandboxConfig.SrcDirIsRO() {
+		srcDirMountFlag = "-R"
+	} else {
+		srcDirMountFlag = "-B" //Read-Write
+	}
+
 	sandboxArgs := []string{
 		// The executable to run
 		"-x", c.Path,
@@ -184,8 +191,8 @@
 		// Mount a writable tmp dir
 		"-B", "/tmp",
 
-		// Mount source are read-write
-		"-B", sandboxConfig.srcDir,
+		// Mount source
+		srcDirMountFlag, sandboxConfig.srcDir,
 
 		//Mount out dir as read-write
 		"-B", sandboxConfig.outDir,
@@ -198,6 +205,18 @@
 		"-q",
 	}
 
+	// Mount srcDir RW allowlists as Read-Write
+	if len(c.config.sandboxConfig.SrcDirRWAllowlist()) > 0 && !c.config.sandboxConfig.SrcDirIsRO() {
+		errMsg := `Product source tree has been set as ReadWrite, RW allowlist not necessary.
+			To recover, either
+			1. Unset BUILD_BROKEN_SRC_DIR_IS_WRITABLE #or
+			2. Unset BUILD_BROKEN_SRC_DIR_RW_ALLOWLIST`
+		c.ctx.Fatalln(errMsg)
+	}
+	for _, srcDirChild := range c.config.sandboxConfig.SrcDirRWAllowlist() {
+		sandboxArgs = append(sandboxArgs, "-B", srcDirChild)
+	}
+
 	if _, err := os.Stat(sandboxConfig.distDir); !os.IsNotExist(err) {
 		//Mount dist dir as read-write if it already exists
 		sandboxArgs = append(sandboxArgs, "-B", sandboxConfig.distDir)
diff --git a/ui/build/sandbox_linux_test.go b/ui/build/sandbox_linux_test.go
new file mode 100644
index 0000000..7bfd750
--- /dev/null
+++ b/ui/build/sandbox_linux_test.go
@@ -0,0 +1,104 @@
+// 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 build
+
+import (
+	"os"
+	"testing"
+)
+
+func TestMain(m *testing.M) {
+	// set src dir of sandbox
+	sandboxConfig.srcDir = "/my/src/dir"
+	os.Exit(m.Run())
+}
+
+func TestMountFlagsSrcDir(t *testing.T) {
+	testCases := []struct {
+		srcDirIsRO         bool
+		expectedSrcDirFlag string
+	}{
+		{
+			srcDirIsRO:         false,
+			expectedSrcDirFlag: "-B",
+		},
+		{
+			srcDirIsRO:         true,
+			expectedSrcDirFlag: "-R",
+		},
+	}
+	for _, testCase := range testCases {
+		c := testCmd()
+		c.config.sandboxConfig.SetSrcDirIsRO(testCase.srcDirIsRO)
+		c.wrapSandbox()
+		if !isExpectedMountFlag(c.Args, sandboxConfig.srcDir, testCase.expectedSrcDirFlag) {
+			t.Error("Mount flag of srcDir is not correct")
+		}
+	}
+}
+
+func TestMountFlagsSrcDirRWAllowlist(t *testing.T) {
+	testCases := []struct {
+		srcDirRWAllowlist []string
+	}{
+		{
+			srcDirRWAllowlist: []string{},
+		},
+		{
+			srcDirRWAllowlist: []string{"my/path"},
+		},
+		{
+			srcDirRWAllowlist: []string{"my/path1", "my/path2"},
+		},
+	}
+	for _, testCase := range testCases {
+		c := testCmd()
+		c.config.sandboxConfig.SetSrcDirIsRO(true)
+		c.config.sandboxConfig.SetSrcDirRWAllowlist(testCase.srcDirRWAllowlist)
+		c.wrapSandbox()
+		for _, allowlistPath := range testCase.srcDirRWAllowlist {
+			if !isExpectedMountFlag(c.Args, allowlistPath, "-B") {
+				t.Error("Mount flag of srcDirRWAllowlist is not correct, expect -B")
+			}
+		}
+	}
+}
+
+// utils for setting up test
+func testConfig() Config {
+	// create a minimal testConfig
+	env := Environment([]string{})
+	sandboxConfig := SandboxConfig{}
+	return Config{&configImpl{environ: &env,
+		sandboxConfig: &sandboxConfig}}
+}
+
+func testCmd() *Cmd {
+	return Command(testContext(), testConfig(), "sandbox_test", "path/to/nsjail")
+}
+
+func isExpectedMountFlag(cmdArgs []string, dirName string, expectedFlag string) bool {
+	indexOfSrcDir := index(cmdArgs, dirName)
+	return cmdArgs[indexOfSrcDir-1] == expectedFlag
+}
+
+func index(arr []string, target string) int {
+	for idx, element := range arr {
+		if element == target {
+			return idx
+		}
+	}
+	panic("element could not be located in input array")
+}
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,
 	}
 }