Merge "Move copying of dex files from dexpreopt_bootjars singleton"
diff --git a/android/apex.go b/android/apex.go
index 60da45b..b01b700 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -203,6 +203,12 @@
 	// apex_available property of the module.
 	AvailableFor(what string) bool
 
+	// AlwaysRequiresPlatformApexVariant allows the implementing module to determine whether an
+	// APEX mutator should always be created for it.
+	//
+	// Returns false by default.
+	AlwaysRequiresPlatformApexVariant() bool
+
 	// Returns true if this module is not available to platform (i.e. apex_available property
 	// doesn't have "//apex_available:platform"), or shouldn't be available to platform, which
 	// is the case when this module depends on other module that isn't available to platform.
@@ -424,6 +430,11 @@
 }
 
 // Implements ApexModule
+func (m *ApexModuleBase) AlwaysRequiresPlatformApexVariant() bool {
+	return false
+}
+
+// Implements ApexModule
 func (m *ApexModuleBase) NotAvailableForPlatform() bool {
 	return m.ApexProperties.NotAvailableForPlatform
 }
diff --git a/android/bazel.go b/android/bazel.go
index 0af4aa0..b1a90d9 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -207,7 +207,7 @@
 		"libc_jemalloc_wrapper", // http://b/187012490, cc_library_static, depends on //external/jemalloc_new:libjemalloc5 (http://b/186828626)
 		"libc_ndk",              // http://b/187013218, cc_library_static, depends on //bionic/libm:libm (http://b/183064661)
 		"libc",                  // http://b/183064430, cc_library, depends on //external/jemalloc_new:libjemalloc5 (http://b/186828626)
-		"libc_bionic_ndk",       // http://b/186822256, cc_library_static, signal.cpp:186:52: error: ISO C++ requires field designators to be specified in declaration order
+		"libc_bionic_ndk",       // http://b/186822256, cc_library_static, fatal error: 'generated_android_ids.h' file not found
 		"libc_malloc_hooks",     // http://b/187016307, cc_library, ld.lld: error: undefined symbol: __malloc_hook
 		"libm",                  // http://b/183064661, cc_library, math.h:25:16: error: unexpected token in argument list
 
diff --git a/android/module.go b/android/module.go
index 9bc27a7..c9a1662 100644
--- a/android/module.go
+++ b/android/module.go
@@ -1500,7 +1500,7 @@
 	var installDeps []*installPathsDepSet
 	var packagingSpecs []*packagingSpecsDepSet
 	ctx.VisitDirectDeps(func(dep Module) {
-		if IsInstallDepNeeded(ctx.OtherModuleDependencyTag(dep)) {
+		if IsInstallDepNeeded(ctx.OtherModuleDependencyTag(dep)) && !dep.IsHideFromMake() {
 			installDeps = append(installDeps, dep.base().installFilesDepSet)
 			packagingSpecs = append(packagingSpecs, dep.base().packagingSpecsDepSet)
 		}
diff --git a/android/packaging.go b/android/packaging.go
index 72c0c17..9065826 100644
--- a/android/packaging.go
+++ b/android/packaging.go
@@ -198,8 +198,8 @@
 	}
 }
 
-// See PackageModule.CopyDepsToZip
-func (p *PackagingBase) CopyDepsToZip(ctx ModuleContext, zipOut WritablePath) (entries []string) {
+// Returns transitive PackagingSpecs from deps
+func (p *PackagingBase) GatherPackagingSpecs(ctx ModuleContext) map[string]PackagingSpec {
 	m := make(map[string]PackagingSpec)
 	ctx.VisitDirectDeps(func(child Module) {
 		if pi, ok := ctx.OtherModuleDependencyTag(child).(PackagingItem); !ok || !pi.IsPackagingItem() {
@@ -211,7 +211,12 @@
 			}
 		}
 	})
+	return m
+}
 
+// See PackageModule.CopyDepsToZip
+func (p *PackagingBase) CopyDepsToZip(ctx ModuleContext, zipOut WritablePath) (entries []string) {
+	m := p.GatherPackagingSpecs(ctx)
 	builder := NewRuleBuilder(pctx, ctx)
 
 	dir := PathForModuleOut(ctx, ".zip")
diff --git a/apex/apex.go b/apex/apex.go
index 2c0df27..26ac457 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -69,10 +69,12 @@
 	ctx.BottomUp("apex_unique", apexUniqueVariationsMutator).Parallel()
 	ctx.BottomUp("apex_test_for_deps", apexTestForDepsMutator).Parallel()
 	ctx.BottomUp("apex_test_for", apexTestForMutator).Parallel()
+	// Run mark_platform_availability before the apexMutator as the apexMutator needs to know whether
+	// it should create a platform variant.
+	ctx.BottomUp("mark_platform_availability", markPlatformAvailability).Parallel()
 	ctx.BottomUp("apex", apexMutator).Parallel()
 	ctx.BottomUp("apex_directly_in_any", apexDirectlyInAnyMutator).Parallel()
 	ctx.BottomUp("apex_flattened", apexFlattenedMutator).Parallel()
-	ctx.BottomUp("mark_platform_availability", markPlatformAvailability).Parallel()
 }
 
 type apexBundleProperties struct {
@@ -1013,9 +1015,8 @@
 		}
 	})
 
-	// Exception 1: stub libraries and native bridge libraries are always available to platform
-	if cc, ok := mctx.Module().(*cc.Module); ok &&
-		(cc.IsStubs() || cc.Target().NativeBridge == android.NativeBridgeEnabled) {
+	// Exception 1: check to see if the module always requires it.
+	if am.AlwaysRequiresPlatformApexVariant() {
 		availableToPlatform = true
 	}
 
@@ -1256,7 +1257,7 @@
 }
 
 // Implements cc.Coverage
-func (a *apexBundle) PreventInstall() {
+func (a *apexBundle) SetPreventInstall() {
 	a.properties.PreventInstall = true
 }
 
diff --git a/bp2build/Android.bp b/bp2build/Android.bp
index abd79f5..3abbc4c 100644
--- a/bp2build/Android.bp
+++ b/bp2build/Android.bp
@@ -20,6 +20,7 @@
         "soong-android",
         "soong-bazel",
         "soong-cc",
+        "soong-cc-config",
         "soong-genrule",
         "soong-python",
         "soong-sh",
diff --git a/bp2build/bp2build.go b/bp2build/bp2build.go
index cf6994f..59c5acd 100644
--- a/bp2build/bp2build.go
+++ b/bp2build/bp2build.go
@@ -24,22 +24,16 @@
 // writing .bzl files that are equivalent to Android.bp files that are capable
 // of being built with Bazel.
 func Codegen(ctx *CodegenContext) CodegenMetrics {
-	outputDir := android.PathForOutput(ctx, "bp2build")
-	android.RemoveAllOutputDir(outputDir)
+	// This directory stores BUILD files that could be eventually checked-in.
+	bp2buildDir := android.PathForOutput(ctx, "bp2build")
+	android.RemoveAllOutputDir(bp2buildDir)
 
 	buildToTargets, metrics := GenerateBazelTargets(ctx, true)
+	bp2buildFiles := CreateBazelFiles(nil, buildToTargets, ctx.mode)
+	writeFiles(ctx, bp2buildDir, bp2buildFiles)
 
-	filesToWrite := CreateBazelFiles(nil, buildToTargets, ctx.mode)
-
-	generatedBuildFiles := []string{}
-	for _, f := range filesToWrite {
-		p := getOrCreateOutputDir(outputDir, ctx, f.Dir).Join(ctx, f.Basename)
-		if err := writeFile(ctx, p, f.Contents); err != nil {
-			panic(fmt.Errorf("Failed to write %q (dir %q) due to %q", f.Basename, f.Dir, err))
-		}
-		// if these generated files are modified, regenerate on next run.
-		generatedBuildFiles = append(generatedBuildFiles, p.String())
-	}
+	soongInjectionDir := android.PathForOutput(ctx, "soong_injection")
+	writeFiles(ctx, soongInjectionDir, CreateSoongInjectionFiles())
 
 	return metrics
 }
@@ -51,6 +45,16 @@
 	return dirPath
 }
 
+// writeFiles materializes a list of BazelFile rooted at outputDir.
+func writeFiles(ctx android.PathContext, outputDir android.OutputPath, files []BazelFile) {
+	for _, f := range files {
+		p := getOrCreateOutputDir(outputDir, ctx, f.Dir).Join(ctx, f.Basename)
+		if err := writeFile(ctx, p, f.Contents); err != nil {
+			panic(fmt.Errorf("Failed to write %q (dir %q) due to %q", f.Basename, f.Dir, err))
+		}
+	}
+}
+
 func writeFile(ctx android.PathContext, pathToFile android.OutputPath, content string) error {
 	// These files are made editable to allow users to modify and iterate on them
 	// in the source tree.
diff --git a/bp2build/cc_library_conversion_test.go b/bp2build/cc_library_conversion_test.go
index 8f060c0..c3f13e6 100644
--- a/bp2build/cc_library_conversion_test.go
+++ b/bp2build/cc_library_conversion_test.go
@@ -353,6 +353,43 @@
 )`},
 		},
 		{
+			description:                        "cc_library configured version script",
+			moduleTypeUnderTest:                "cc_library",
+			moduleTypeUnderTestFactory:         cc.LibraryFactory,
+			moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+			depsMutators:                       []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build},
+			dir:                                "foo/bar",
+			filesystem: map[string]string{
+				"foo/bar/Android.bp": `
+		cc_library {
+		   name: "a",
+		   srcs: ["a.cpp"],
+		   arch: {
+		     arm: {
+		       version_script: "arm.map",
+		     },
+		     arm64: {
+		       version_script: "arm64.map",
+		     },
+		   },
+
+		   bazel_module: { bp2build_available: true },
+		}
+		`,
+			},
+			bp: soongCcLibraryPreamble,
+			expectedBazelTargets: []string{`cc_library(
+    name = "a",
+    copts = ["-Ifoo/bar"],
+    srcs = ["a.cpp"],
+    version_script = select({
+        "//build/bazel/platforms/arch:arm": "arm.map",
+        "//build/bazel/platforms/arch:arm64": "arm64.map",
+        "//conditions:default": None,
+    }),
+)`},
+		},
+		{
 			description:                        "cc_library shared_libs",
 			moduleTypeUnderTest:                "cc_library",
 			moduleTypeUnderTestFactory:         cc.LibraryFactory,
diff --git a/bp2build/configurability.go b/bp2build/configurability.go
index 95a2747..2b8f6cc 100644
--- a/bp2build/configurability.go
+++ b/bp2build/configurability.go
@@ -31,8 +31,19 @@
 }
 
 func getLabelValue(label bazel.LabelAttribute) (reflect.Value, selects, selects) {
-	value := reflect.ValueOf(label.Value)
-	return value, nil, nil
+	var value reflect.Value
+	var archSelects selects
+
+	if label.HasConfigurableValues() {
+		archSelects = map[string]reflect.Value{}
+		for arch, selectKey := range bazel.PlatformArchMap {
+			archSelects[selectKey] = reflect.ValueOf(label.GetValueForArch(arch))
+		}
+	} else {
+		value = reflect.ValueOf(label.Value)
+	}
+
+	return value, archSelects, nil
 }
 
 func getLabelListValues(list bazel.LabelListAttribute) (reflect.Value, selects, selects) {
diff --git a/bp2build/conversion.go b/bp2build/conversion.go
index d67ab3d..eb83b38 100644
--- a/bp2build/conversion.go
+++ b/bp2build/conversion.go
@@ -2,6 +2,7 @@
 
 import (
 	"android/soong/android"
+	"android/soong/cc/config"
 	"fmt"
 	"reflect"
 	"sort"
@@ -16,6 +17,15 @@
 	Contents string
 }
 
+func CreateSoongInjectionFiles() []BazelFile {
+	var files []BazelFile
+
+	files = append(files, newFile("cc_toolchain", "BUILD", "")) // Creates a //cc_toolchain package.
+	files = append(files, newFile("cc_toolchain", "constants.bzl", config.BazelCcToolchainVars()))
+
+	return files
+}
+
 func CreateBazelFiles(
 	ruleShims map[string]RuleShim,
 	buildToTargets map[string]BazelTargets,
diff --git a/bp2build/conversion_test.go b/bp2build/conversion_test.go
index 262a488..a08c03d 100644
--- a/bp2build/conversion_test.go
+++ b/bp2build/conversion_test.go
@@ -79,9 +79,33 @@
 	}
 }
 
-func TestCreateBazelFiles_Bp2Build_CreatesNoFilesWithNoTargets(t *testing.T) {
-	files := CreateBazelFiles(map[string]RuleShim{}, map[string]BazelTargets{}, Bp2Build)
-	if len(files) != 0 {
-		t.Errorf("Expected no files, got %d", len(files))
+func TestCreateBazelFiles_Bp2Build_CreatesDefaultFiles(t *testing.T) {
+	files := CreateSoongInjectionFiles()
+
+	expectedFilePaths := []bazelFilepath{
+		{
+			dir:      "cc_toolchain",
+			basename: "BUILD",
+		},
+		{
+			dir:      "cc_toolchain",
+			basename: "constants.bzl",
+		},
+	}
+
+	if len(files) != len(expectedFilePaths) {
+		t.Errorf("Expected %d file, got %d", len(expectedFilePaths), len(files))
+	}
+
+	for i := range files {
+		actualFile, expectedFile := files[i], expectedFilePaths[i]
+
+		if actualFile.Dir != expectedFile.dir || actualFile.Basename != expectedFile.basename {
+			t.Errorf("Did not find expected file %s/%s", actualFile.Dir, actualFile.Basename)
+		}
+
+		if expectedFile.basename != "BUILD" && actualFile.Contents == "" {
+			t.Errorf("Contents of %s unexpected empty.", actualFile)
+		}
 	}
 }
diff --git a/cc/cc.go b/cc/cc.go
index 16a49d3..c5552d6 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -841,6 +841,10 @@
 	c.Properties.HideFromMake = true
 }
 
+func (c *Module) HiddenFromMake() bool {
+	return c.Properties.HideFromMake
+}
+
 func (c *Module) Toc() android.OptionalPath {
 	if c.linker != nil {
 		if library, ok := c.linker.(libraryInterface); ok {
@@ -1088,12 +1092,6 @@
 	return false
 }
 
-// Returns true if the module is using VNDK libraries instead of the libraries in /system/lib or /system/lib64.
-// "product" and "vendor" variant modules return true for this function.
-// When BOARD_VNDK_VERSION is set, vendor variants of "vendor_available: true", "vendor: true",
-// "soc_specific: true" and more vendor installed modules are included here.
-// When PRODUCT_PRODUCT_VNDK_VERSION is set, product variants of "product_available: true" or
-// "product_specific: true" modules are included here.
 func (c *Module) UseVndk() bool {
 	return c.Properties.VndkVersion != ""
 }
@@ -1141,6 +1139,18 @@
 	return c.VendorProperties.IsVendorPublicLibrary
 }
 
+func (c *Module) HasLlndkStubs() bool {
+	lib := moduleLibraryInterface(c)
+	return lib != nil && lib.hasLLNDKStubs()
+}
+
+func (c *Module) StubsVersion() string {
+	if lib, ok := c.linker.(versionedInterface); ok {
+		return lib.stubsVersion()
+	}
+	panic(fmt.Errorf("StubsVersion called on non-versioned module: %q", c.BaseModuleName()))
+}
+
 // isImplementationForLLNDKPublic returns true for any variant of a cc_library that has LLNDK stubs
 // and does not set llndk.vendor_available: false.
 func (c *Module) isImplementationForLLNDKPublic() bool {
@@ -1185,7 +1195,7 @@
 	return false
 }
 
-func (c *Module) isVndkSp() bool {
+func (c *Module) IsVndkSp() bool {
 	if vndkdep := c.vndkdep; vndkdep != nil {
 		return vndkdep.isVndkSp()
 	}
@@ -1204,7 +1214,7 @@
 }
 
 func (c *Module) MustUseVendorVariant() bool {
-	return c.isVndkSp() || c.Properties.MustUseVendorVariant
+	return c.IsVndkSp() || c.Properties.MustUseVendorVariant
 }
 
 func (c *Module) getVndkExtendsModuleName() string {
@@ -1343,11 +1353,11 @@
 }
 
 func (ctx *moduleContextImpl) binary() bool {
-	return ctx.mod.binary()
+	return ctx.mod.Binary()
 }
 
 func (ctx *moduleContextImpl) object() bool {
-	return ctx.mod.object()
+	return ctx.mod.Object()
 }
 
 func (ctx *moduleContextImpl) canUseSdk() bool {
@@ -1445,7 +1455,7 @@
 }
 
 func (ctx *moduleContextImpl) isVndkSp() bool {
-	return ctx.mod.isVndkSp()
+	return ctx.mod.IsVndkSp()
 }
 
 func (ctx *moduleContextImpl) IsVndkExt() bool {
@@ -1786,7 +1796,7 @@
 		// glob exported headers for snapshot, if BOARD_VNDK_VERSION is current or
 		// RECOVERY_SNAPSHOT_VERSION is current.
 		if i, ok := c.linker.(snapshotLibraryInterface); ok {
-			if shouldCollectHeadersForSnapshot(ctx, c, apexInfo) {
+			if ShouldCollectHeadersForSnapshot(ctx, c, apexInfo) {
 				i.collectHeadersForSnapshot(ctx)
 			}
 		}
@@ -1799,7 +1809,7 @@
 		// modules can be hidden from make as some are needed for resolving make side
 		// dependencies.
 		c.HideFromMake()
-	} else if !c.installable(apexInfo) {
+	} else if !installable(c, apexInfo) {
 		c.SkipInstall()
 	}
 
@@ -2451,7 +2461,7 @@
 			return true
 		}
 
-		if to.isVndkSp() || to.IsLlndk() {
+		if to.IsVndkSp() || to.IsLlndk() {
 			return false
 		}
 
@@ -3064,7 +3074,7 @@
 	return false
 }
 
-func (c *Module) binary() bool {
+func (c *Module) Binary() bool {
 	if b, ok := c.linker.(interface {
 		binary() bool
 	}); ok {
@@ -3073,7 +3083,7 @@
 	return false
 }
 
-func (c *Module) object() bool {
+func (c *Module) Object() bool {
 	if o, ok := c.linker.(interface {
 		object() bool
 	}); ok {
@@ -3155,18 +3165,25 @@
 	}
 }
 
-// Return true if the module is ever installable.
 func (c *Module) EverInstallable() bool {
 	return c.installer != nil &&
 		// Check to see whether the module is actually ever installable.
 		c.installer.everInstallable()
 }
 
-func (c *Module) installable(apexInfo android.ApexInfo) bool {
+func (c *Module) PreventInstall() bool {
+	return c.Properties.PreventInstall
+}
+
+func (c *Module) Installable() *bool {
+	return c.Properties.Installable
+}
+
+func installable(c LinkableInterface, apexInfo android.ApexInfo) bool {
 	ret := c.EverInstallable() &&
 		// Check to see whether the module has been configured to not be installed.
-		proptools.BoolDefault(c.Properties.Installable, true) &&
-		!c.Properties.PreventInstall && c.outputFile.Valid()
+		proptools.BoolDefault(c.Installable(), true) &&
+		!c.PreventInstall() && c.OutputFile().Valid()
 
 	// The platform variant doesn't need further condition. Apex variants however might not
 	// be installable because it will likely to be included in the APEX and won't appear
@@ -3279,6 +3296,12 @@
 	return nil
 }
 
+// Implements android.ApexModule
+func (c *Module) AlwaysRequiresPlatformApexVariant() bool {
+	// stub libraries and native bridge libraries are always available to platform
+	return c.IsStubs() || c.Target().NativeBridge == android.NativeBridgeEnabled
+}
+
 //
 // Defaults
 //
diff --git a/cc/cc_test.go b/cc/cc_test.go
index e9daf33..d82619a 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -381,8 +381,8 @@
 	if !mod.IsVndk() {
 		t.Errorf("%q IsVndk() must equal to true", name)
 	}
-	if mod.isVndkSp() != isVndkSp {
-		t.Errorf("%q isVndkSp() must equal to %t", name, isVndkSp)
+	if mod.IsVndkSp() != isVndkSp {
+		t.Errorf("%q IsVndkSp() must equal to %t", name, isVndkSp)
 	}
 
 	// Check VNDK extension properties.
diff --git a/cc/config/Android.bp b/cc/config/Android.bp
index 5ef247d..e4a8b62 100644
--- a/cc/config/Android.bp
+++ b/cc/config/Android.bp
@@ -10,6 +10,7 @@
         "soong-remoteexec",
     ],
     srcs: [
+        "bp2build.go",
         "clang.go",
         "global.go",
         "tidy.go",
@@ -31,6 +32,7 @@
         "arm64_linux_host.go",
     ],
     testSrcs: [
+        "bp2build_test.go",
         "tidy_test.go",
     ],
 }
diff --git a/cc/config/bp2build.go b/cc/config/bp2build.go
new file mode 100644
index 0000000..16892e6
--- /dev/null
+++ b/cc/config/bp2build.go
@@ -0,0 +1,148 @@
+// 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 config
+
+import (
+	"android/soong/android"
+	"fmt"
+	"regexp"
+	"strings"
+)
+
+// Helpers for exporting cc configuration information to Bazel.
+
+var (
+	// Map containing toolchain variables that are independent of the
+	// environment variables of the build.
+	exportedVars = exportedVariablesMap{}
+)
+
+// variableValue is a string slice because the exported variables are all lists
+// of string, and it's simpler to manipulate string lists before joining them
+// into their final string representation.
+type variableValue []string
+
+// envDependentVariable is a toolchain variable computed based on an environment variable.
+type exportedVariablesMap map[string]variableValue
+
+func (m exportedVariablesMap) Set(k string, v variableValue) {
+	m[k] = v
+}
+
+// Convenience function to declare a static variable and export it to Bazel's cc_toolchain.
+func staticVariableExportedToBazel(name string, value []string) {
+	pctx.StaticVariable(name, strings.Join(value, " "))
+	exportedVars.Set(name, variableValue(value))
+}
+
+// BazelCcToolchainVars generates bzl file content containing variables for
+// Bazel's cc_toolchain configuration.
+func BazelCcToolchainVars() string {
+	ret := "# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT.\n\n"
+
+	// Ensure that string s has no invalid characters to be generated into the bzl file.
+	validateCharacters := func(s string) string {
+		for _, c := range []string{`\n`, `"`, `\`} {
+			if strings.Contains(s, c) {
+				panic(fmt.Errorf("%s contains illegal character %s", s, c))
+			}
+		}
+		return s
+	}
+
+	// For each exported variable, recursively expand elements in the variableValue
+	// list to ensure that interpolated variables are expanded according to their values
+	// in the exportedVars scope.
+	for _, k := range android.SortedStringKeys(exportedVars) {
+		variableValue := exportedVars[k]
+		var expandedVars []string
+		for _, v := range variableValue {
+			expandedVars = append(expandedVars, expandVar(v, exportedVars)...)
+		}
+		// Build the list for this variable.
+		list := "["
+		for _, flag := range expandedVars {
+			list += fmt.Sprintf("\n    \"%s\",", validateCharacters(flag))
+		}
+		list += "\n]"
+		// Assign the list as a bzl-private variable; this variable will be exported
+		// out through a constants struct later.
+		ret += fmt.Sprintf("_%s = %s\n", k, list)
+		ret += "\n"
+	}
+
+	// Build the exported constants struct.
+	ret += "constants = struct(\n"
+	for _, k := range android.SortedStringKeys(exportedVars) {
+		ret += fmt.Sprintf("    %s = _%s,\n", k, k)
+	}
+	ret += ")"
+	return ret
+}
+
+// expandVar recursively expand interpolated variables in the exportedVars scope.
+//
+// We're using a string slice to track the seen variables to avoid
+// stackoverflow errors with infinite recursion. it's simpler to use a
+// string slice than to handle a pass-by-referenced map, which would make it
+// quite complex to track depth-first interpolations. It's also unlikely the
+// interpolation stacks are deep (n > 1).
+func expandVar(toExpand string, exportedVars map[string]variableValue) []string {
+	// e.g. "${ClangExternalCflags}"
+	r := regexp.MustCompile(`\${([a-zA-Z0-9_]+)}`)
+
+	// Internal recursive function.
+	var expandVarInternal func(string, map[string]bool) []string
+	expandVarInternal = func(toExpand string, seenVars map[string]bool) []string {
+		var ret []string
+		for _, v := range strings.Split(toExpand, " ") {
+			matches := r.FindStringSubmatch(v)
+			if len(matches) == 0 {
+				return []string{v}
+			}
+
+			if len(matches) != 2 {
+				panic(fmt.Errorf(
+					"Expected to only match 1 subexpression in %s, got %d",
+					v,
+					len(matches)-1))
+			}
+
+			// Index 1 of FindStringSubmatch contains the subexpression match
+			// (variable name) of the capture group.
+			variable := matches[1]
+			// toExpand contains a variable.
+			if _, ok := seenVars[variable]; ok {
+				panic(fmt.Errorf(
+					"Unbounded recursive interpolation of variable: %s", variable))
+			}
+			// A map is passed-by-reference. Create a new map for
+			// this scope to prevent variables seen in one depth-first expansion
+			// to be also treated as "seen" in other depth-first traversals.
+			newSeenVars := map[string]bool{}
+			for k := range seenVars {
+				newSeenVars[k] = true
+			}
+			newSeenVars[variable] = true
+			unexpandedVars := exportedVars[variable]
+			for _, unexpandedVar := range unexpandedVars {
+				ret = append(ret, expandVarInternal(unexpandedVar, newSeenVars)...)
+			}
+		}
+		return ret
+	}
+
+	return expandVarInternal(toExpand, map[string]bool{})
+}
diff --git a/cc/config/bp2build_test.go b/cc/config/bp2build_test.go
new file mode 100644
index 0000000..7744b4b
--- /dev/null
+++ b/cc/config/bp2build_test.go
@@ -0,0 +1,91 @@
+// 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 config
+
+import (
+	"testing"
+)
+
+func TestExpandVars(t *testing.T) {
+	testCases := []struct {
+		description    string
+		exportedVars   map[string]variableValue
+		toExpand       string
+		expectedValues []string
+	}{
+		{
+			description: "single level expansion",
+			exportedVars: map[string]variableValue{
+				"foo": variableValue([]string{"bar"}),
+			},
+			toExpand:       "${foo}",
+			expectedValues: []string{"bar"},
+		},
+		{
+			description: "double level expansion",
+			exportedVars: map[string]variableValue{
+				"foo": variableValue([]string{"${bar}"}),
+				"bar": variableValue([]string{"baz"}),
+			},
+			toExpand:       "${foo}",
+			expectedValues: []string{"baz"},
+		},
+		{
+			description: "double level expansion with a literal",
+			exportedVars: map[string]variableValue{
+				"a": variableValue([]string{"${b}", "c"}),
+				"b": variableValue([]string{"d"}),
+			},
+			toExpand:       "${a}",
+			expectedValues: []string{"d", "c"},
+		},
+		{
+			description: "double level expansion, with two variables in a string",
+			exportedVars: map[string]variableValue{
+				"a": variableValue([]string{"${b} ${c}"}),
+				"b": variableValue([]string{"d"}),
+				"c": variableValue([]string{"e"}),
+			},
+			toExpand:       "${a}",
+			expectedValues: []string{"d", "e"},
+		},
+		{
+			description: "triple level expansion with two variables in a string",
+			exportedVars: map[string]variableValue{
+				"a": variableValue([]string{"${b} ${c}"}),
+				"b": variableValue([]string{"${c}", "${d}"}),
+				"c": variableValue([]string{"${d}"}),
+				"d": variableValue([]string{"foo"}),
+			},
+			toExpand:       "${a}",
+			expectedValues: []string{"foo", "foo", "foo"},
+		},
+	}
+
+	for _, testCase := range testCases {
+		t.Run(testCase.description, func(t *testing.T) {
+			output := expandVar(testCase.toExpand, testCase.exportedVars)
+			if len(output) != len(testCase.expectedValues) {
+				t.Errorf("Expected %d values, got %d", len(testCase.expectedValues), len(output))
+			}
+			for i, actual := range output {
+				expectedValue := testCase.expectedValues[i]
+				if actual != expectedValue {
+					t.Errorf("Actual value '%s' doesn't match expected value '%s'", actual, expectedValue)
+				}
+			}
+		})
+	}
+}
diff --git a/cc/config/clang.go b/cc/config/clang.go
index 5e46d5a..4fbb9c3 100644
--- a/cc/config/clang.go
+++ b/cc/config/clang.go
@@ -98,7 +98,7 @@
 }
 
 func init() {
-	pctx.StaticVariable("ClangExtraCflags", strings.Join([]string{
+	staticVariableExportedToBazel("ClangExtraCflags", []string{
 		"-D__compiler_offsetof=__builtin_offsetof",
 
 		// Emit address-significance table which allows linker to perform safe ICF. Clang does
@@ -151,9 +151,9 @@
 		// This macro allows the bionic versioning.h to indirectly determine whether the
 		// option -Wunguarded-availability is on or not.
 		"-D__ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__",
-	}, " "))
+	})
 
-	pctx.StaticVariable("ClangExtraCppflags", strings.Join([]string{
+	staticVariableExportedToBazel("ClangExtraCppflags", []string{
 		// -Wimplicit-fallthrough is not enabled by -Wall.
 		"-Wimplicit-fallthrough",
 
@@ -162,13 +162,11 @@
 
 		// libc++'s math.h has an #include_next outside of system_headers.
 		"-Wno-gnu-include-next",
-	}, " "))
+	})
 
-	pctx.StaticVariable("ClangExtraTargetCflags", strings.Join([]string{
-		"-nostdlibinc",
-	}, " "))
+	staticVariableExportedToBazel("ClangExtraTargetCflags", []string{"-nostdlibinc"})
 
-	pctx.StaticVariable("ClangExtraNoOverrideCflags", strings.Join([]string{
+	staticVariableExportedToBazel("ClangExtraNoOverrideCflags", []string{
 		"-Werror=address-of-temporary",
 		// Bug: http://b/29823425 Disable -Wnull-dereference until the
 		// new cases detected by this warning in Clang r271374 are
@@ -203,11 +201,11 @@
 		"-Wno-non-c-typedef-for-linkage", // http://b/161304145
 		// New warnings to be fixed after clang-r407598
 		"-Wno-string-concatenation", // http://b/175068488
-	}, " "))
+	})
 
 	// Extra cflags for external third-party projects to disable warnings that
 	// are infeasible to fix in all the external projects and their upstream repos.
-	pctx.StaticVariable("ClangExtraExternalCflags", strings.Join([]string{
+	staticVariableExportedToBazel("ClangExtraExternalCflags", []string{
 		"-Wno-enum-compare",
 		"-Wno-enum-compare-switch",
 
@@ -228,7 +226,7 @@
 
 		// http://b/165945989
 		"-Wno-psabi",
-	}, " "))
+	})
 }
 
 func ClangFilterUnknownCflags(cflags []string) []string {
diff --git a/cc/config/global.go b/cc/config/global.go
index 23106ec..59fe8e1 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -165,13 +165,25 @@
 		commonGlobalCflags = append(commonGlobalCflags, "-fdebug-prefix-map=/proc/self/cwd=")
 	}
 
-	pctx.StaticVariable("CommonGlobalConlyflags", strings.Join(commonGlobalConlyflags, " "))
-	pctx.StaticVariable("DeviceGlobalCppflags", strings.Join(deviceGlobalCppflags, " "))
-	pctx.StaticVariable("DeviceGlobalLdflags", strings.Join(deviceGlobalLdflags, " "))
-	pctx.StaticVariable("DeviceGlobalLldflags", strings.Join(deviceGlobalLldflags, " "))
-	pctx.StaticVariable("HostGlobalCppflags", strings.Join(hostGlobalCppflags, " "))
-	pctx.StaticVariable("HostGlobalLdflags", strings.Join(hostGlobalLdflags, " "))
-	pctx.StaticVariable("HostGlobalLldflags", strings.Join(hostGlobalLldflags, " "))
+	staticVariableExportedToBazel("CommonGlobalConlyflags", commonGlobalConlyflags)
+	staticVariableExportedToBazel("DeviceGlobalCppflags", deviceGlobalCppflags)
+	staticVariableExportedToBazel("DeviceGlobalLdflags", deviceGlobalLdflags)
+	staticVariableExportedToBazel("DeviceGlobalLldflags", deviceGlobalLldflags)
+	staticVariableExportedToBazel("HostGlobalCppflags", hostGlobalCppflags)
+	staticVariableExportedToBazel("HostGlobalLdflags", hostGlobalLdflags)
+	staticVariableExportedToBazel("HostGlobalLldflags", hostGlobalLldflags)
+
+	// Export the static default CommonClangGlobalCflags to Bazel.
+	// TODO(187086342): handle cflags that are set in VariableFuncs.
+	commonClangGlobalCFlags := append(
+		ClangFilterUnknownCflags(commonGlobalCflags),
+		[]string{
+			"${ClangExtraCflags}",
+			// Default to zero initialization.
+			"-ftrivial-auto-var-init=zero",
+			"-enable-trivial-auto-var-init-zero-knowing-it-will-be-removed-from-clang",
+		}...)
+	exportedVars.Set("CommonClangGlobalCflags", variableValue(commonClangGlobalCFlags))
 
 	pctx.VariableFunc("CommonClangGlobalCflags", func(ctx android.PackageVarContext) string {
 		flags := ClangFilterUnknownCflags(commonGlobalCflags)
@@ -190,26 +202,26 @@
 			// Default to zero initialization.
 			flags = append(flags, "-ftrivial-auto-var-init=zero -enable-trivial-auto-var-init-zero-knowing-it-will-be-removed-from-clang")
 		}
-
 		return strings.Join(flags, " ")
 	})
 
+	// Export the static default DeviceClangGlobalCflags to Bazel.
+	// TODO(187086342): handle cflags that are set in VariableFuncs.
+	deviceClangGlobalCflags := append(ClangFilterUnknownCflags(deviceGlobalCflags), "${ClangExtraTargetCflags}")
+	exportedVars.Set("DeviceClangGlobalCflags", variableValue(deviceClangGlobalCflags))
+
 	pctx.VariableFunc("DeviceClangGlobalCflags", func(ctx android.PackageVarContext) string {
 		if ctx.Config().Fuchsia() {
 			return strings.Join(ClangFilterUnknownCflags(deviceGlobalCflags), " ")
 		} else {
-			return strings.Join(append(ClangFilterUnknownCflags(deviceGlobalCflags), "${ClangExtraTargetCflags}"), " ")
+			return strings.Join(deviceClangGlobalCflags, " ")
 		}
 	})
-	pctx.StaticVariable("HostClangGlobalCflags",
-		strings.Join(ClangFilterUnknownCflags(hostGlobalCflags), " "))
-	pctx.StaticVariable("NoOverrideClangGlobalCflags",
-		strings.Join(append(ClangFilterUnknownCflags(noOverrideGlobalCflags), "${ClangExtraNoOverrideCflags}"), " "))
 
-	pctx.StaticVariable("CommonClangGlobalCppflags",
-		strings.Join(append(ClangFilterUnknownCflags(commonGlobalCppflags), "${ClangExtraCppflags}"), " "))
-
-	pctx.StaticVariable("ClangExternalCflags", "${ClangExtraExternalCflags}")
+	staticVariableExportedToBazel("HostClangGlobalCflags", ClangFilterUnknownCflags(hostGlobalCflags))
+	staticVariableExportedToBazel("NoOverrideClangGlobalCflags", append(ClangFilterUnknownCflags(noOverrideGlobalCflags), "${ClangExtraNoOverrideCflags}"))
+	staticVariableExportedToBazel("CommonClangGlobalCppflags", append(ClangFilterUnknownCflags(commonGlobalCppflags), "${ClangExtraCppflags}"))
+	staticVariableExportedToBazel("ClangExternalCflags", []string{"${ClangExtraExternalCflags}"})
 
 	// Everything in these lists is a crime against abstraction and dependency tracking.
 	// Do not add anything to this list.
diff --git a/cc/coverage.go b/cc/coverage.go
index 5b5ccf2..b2788ec 100644
--- a/cc/coverage.go
+++ b/cc/coverage.go
@@ -203,7 +203,7 @@
 type Coverage interface {
 	android.Module
 	IsNativeCoverageNeeded(ctx android.BaseModuleContext) bool
-	PreventInstall()
+	SetPreventInstall()
 	HideFromMake()
 	MarkAsCoverageVariant(bool)
 	EnableCoverageIfNeeded()
@@ -236,7 +236,7 @@
 		// to an APEX via 'data' property.
 		m := mctx.CreateVariations("", "cov")
 		m[0].(Coverage).MarkAsCoverageVariant(false)
-		m[0].(Coverage).PreventInstall()
+		m[0].(Coverage).SetPreventInstall()
 		m[0].(Coverage).HideFromMake()
 
 		m[1].(Coverage).MarkAsCoverageVariant(true)
diff --git a/cc/linkable.go b/cc/linkable.go
index 40a9d8b..b583b69 100644
--- a/cc/linkable.go
+++ b/cc/linkable.go
@@ -48,6 +48,20 @@
 	// SanitizerSupported returns true if a sanitizer type is supported by this modules compiler.
 	SanitizerSupported(t SanitizerType) bool
 
+	// MinimalRuntimeDep returns true if this module needs to link the minimal UBSan runtime,
+	// either because it requires it or because a dependent module which requires it to be linked in this module.
+	MinimalRuntimeDep() bool
+
+	// UbsanRuntimeDep returns true if this module needs to link the full UBSan runtime,
+	// either because it requires it or because a dependent module which requires it to be linked in this module.
+	UbsanRuntimeDep() bool
+
+	// UbsanRuntimeNeeded returns true if the full UBSan runtime is required by this module.
+	UbsanRuntimeNeeded() bool
+
+	// MinimalRuntimeNeeded returns true if the minimal UBSan runtime is required by this module
+	MinimalRuntimeNeeded() bool
+
 	// SanitizableDepTagChecker returns a SantizableDependencyTagChecker function type.
 	SanitizableDepTagChecker() SantizableDependencyTagChecker
 }
@@ -60,14 +74,42 @@
 // implementation should handle tags from both.
 type SantizableDependencyTagChecker func(tag blueprint.DependencyTag) bool
 
+// Snapshottable defines those functions necessary for handling module snapshots.
+type Snapshottable interface {
+	// SnapshotHeaders returns a list of header paths provided by this module.
+	SnapshotHeaders() android.Paths
+
+	// ExcludeFromVendorSnapshot returns true if this module should be otherwise excluded from the vendor snapshot.
+	ExcludeFromVendorSnapshot() bool
+
+	// ExcludeFromRecoverySnapshot returns true if this module should be otherwise excluded from the recovery snapshot.
+	ExcludeFromRecoverySnapshot() bool
+
+	// SnapshotLibrary returns true if this module is a snapshot library.
+	IsSnapshotLibrary() bool
+
+	// SnapshotRuntimeLibs returns a list of libraries needed by this module at runtime but which aren't build dependencies.
+	SnapshotRuntimeLibs() []string
+
+	// SnapshotSharedLibs returns the list of shared library dependencies for this module.
+	SnapshotSharedLibs() []string
+
+	// IsSnapshotPrebuilt returns true if this module is a snapshot prebuilt.
+	IsSnapshotPrebuilt() bool
+}
+
 // LinkableInterface is an interface for a type of module that is linkable in a C++ library.
 type LinkableInterface interface {
 	android.Module
+	Snapshottable
 
 	Module() android.Module
 	CcLibrary() bool
 	CcLibraryInterface() bool
 
+	// BaseModuleName returns the android.ModuleBase.BaseModuleName() value for this module.
+	BaseModuleName() string
+
 	OutputFile() android.OptionalPath
 	CoverageFiles() android.Paths
 
@@ -79,9 +121,6 @@
 	BuildSharedVariant() bool
 	SetStatic()
 	SetShared()
-	Static() bool
-	Shared() bool
-	Header() bool
 	IsPrebuilt() bool
 	Toc() android.OptionalPath
 
@@ -106,13 +145,29 @@
 	// IsLlndkPublic returns true only for LLNDK (public) libs.
 	IsLlndkPublic() bool
 
+	// HasLlndkStubs returns true if this library has a variant that will build LLNDK stubs.
+	HasLlndkStubs() bool
+
 	// NeedsLlndkVariants returns true if this module has LLNDK stubs or provides LLNDK headers.
 	NeedsLlndkVariants() bool
 
 	// NeedsVendorPublicLibraryVariants returns true if this module has vendor public library stubs.
 	NeedsVendorPublicLibraryVariants() bool
 
+	//StubsVersion returns the stubs version for this module.
+	StubsVersion() string
+
+	// UseVndk returns true if the module is using VNDK libraries instead of the libraries in /system/lib or /system/lib64.
+	// "product" and "vendor" variant modules return true for this function.
+	// When BOARD_VNDK_VERSION is set, vendor variants of "vendor_available: true", "vendor: true",
+	// "soc_specific: true" and more vendor installed modules are included here.
+	// When PRODUCT_PRODUCT_VNDK_VERSION is set, product variants of "vendor_available: true" or
+	// "product_specific: true" modules are included here.
 	UseVndk() bool
+
+	// IsVndkSp returns true if this is a VNDK-SP module.
+	IsVndkSp() bool
+
 	MustUseVendorVariant() bool
 	IsVndk() bool
 	IsVndkExt() bool
@@ -140,6 +195,51 @@
 	// KernelHeadersDecorator returns true if this is a kernel headers decorator module.
 	// This is specific to cc and should always return false for all other packages.
 	KernelHeadersDecorator() bool
+
+	// HiddenFromMake returns true if this module is hidden from Make.
+	HiddenFromMake() bool
+
+	// RelativeInstallPath returns the relative install path for this module.
+	RelativeInstallPath() string
+
+	// Binary returns true if this is a binary module.
+	Binary() bool
+
+	// Object returns true if this is an object module.
+	Object() bool
+
+	// Rlib returns true if this is an rlib module.
+	Rlib() bool
+
+	// Dylib returns true if this is an dylib module.
+	Dylib() bool
+
+	// Static returns true if this is a static library module.
+	Static() bool
+
+	// Shared returns true if this is a shared library module.
+	Shared() bool
+
+	// Header returns true if this is a library headers module.
+	Header() bool
+
+	// EverInstallable returns true if the module is ever installable
+	EverInstallable() bool
+
+	// PreventInstall returns true if this module is prevented from installation.
+	PreventInstall() bool
+
+	// InstallInData returns true if this module is installed in data.
+	InstallInData() bool
+
+	// Installable returns a bool pointer to the module installable property.
+	Installable() *bool
+
+	// Symlinks returns a list of symlinks that should be created for this module.
+	Symlinks() []string
+
+	// VndkVersion returns the VNDK version string for this module.
+	VndkVersion() string
 }
 
 var (
diff --git a/cc/sabi.go b/cc/sabi.go
index c0eb57c..384dcc1 100644
--- a/cc/sabi.go
+++ b/cc/sabi.go
@@ -84,7 +84,7 @@
 		return "LLNDK"
 	}
 	if m.UseVndk() && m.IsVndk() && !m.IsVndkPrivate() {
-		if m.isVndkSp() {
+		if m.IsVndkSp() {
 			if m.IsVndkExt() {
 				return "VNDK-SP-ext"
 			} else {
diff --git a/cc/sanitize.go b/cc/sanitize.go
index 397121e..605a8d0 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -1075,7 +1075,7 @@
 			sanitizers = append(sanitizers, "shadow-call-stack")
 		}
 
-		if Bool(c.sanitize.Properties.Sanitize.Memtag_heap) && c.binary() {
+		if Bool(c.sanitize.Properties.Sanitize.Memtag_heap) && c.Binary() {
 			noteDep := "note_memtag_heap_async"
 			if Bool(c.sanitize.Properties.Sanitize.Diag.Memtag_heap) {
 				noteDep = "note_memtag_heap_sync"
@@ -1208,6 +1208,14 @@
 	AddSanitizerDependencies(ctx android.BottomUpMutatorContext, sanitizerName string)
 }
 
+func (c *Module) MinimalRuntimeDep() bool {
+	return c.sanitize.Properties.MinimalRuntimeDep
+}
+
+func (c *Module) UbsanRuntimeDep() bool {
+	return c.sanitize.Properties.UbsanRuntimeDep
+}
+
 func (c *Module) SanitizePropDefined() bool {
 	return c.sanitize != nil
 }
@@ -1441,6 +1449,14 @@
 	return false
 }
 
+func (m *Module) UbsanRuntimeNeeded() bool {
+	return enableUbsanRuntime(m.sanitize)
+}
+
+func (m *Module) MinimalRuntimeNeeded() bool {
+	return enableMinimalRuntime(m.sanitize)
+}
+
 func enableUbsanRuntime(sanitize *sanitize) bool {
 	return Bool(sanitize.Properties.Sanitize.Diag.Integer_overflow) ||
 		Bool(sanitize.Properties.Sanitize.Diag.Undefined) ||
diff --git a/cc/snapshot_prebuilt.go b/cc/snapshot_prebuilt.go
index 885a0ce..0b1147e 100644
--- a/cc/snapshot_prebuilt.go
+++ b/cc/snapshot_prebuilt.go
@@ -35,12 +35,12 @@
 	// Function that returns true if the module is included in this image.
 	// Using a function return instead of a value to prevent early
 	// evalution of a function that may be not be defined.
-	inImage(m *Module) func() bool
+	inImage(m LinkableInterface) func() bool
 
 	// Returns true if the module is private and must not be included in the
 	// snapshot. For example VNDK-private modules must return true for the
 	// vendor snapshots. But false for the recovery snapshots.
-	private(m *Module) bool
+	private(m LinkableInterface) bool
 
 	// Returns true if a dir under source tree is an SoC-owned proprietary
 	// directory, such as device/, vendor/, etc.
@@ -56,7 +56,7 @@
 	// Whether a given module has been explicitly excluded from the
 	// snapshot, e.g., using the exclude_from_vendor_snapshot or
 	// exclude_from_recovery_snapshot properties.
-	excludeFromSnapshot(m *Module) bool
+	excludeFromSnapshot(m LinkableInterface) bool
 
 	// Returns true if the build is using a snapshot for this image.
 	isUsingSnapshot(cfg android.DeviceConfig) bool
@@ -125,11 +125,11 @@
 	return ctx.DeviceConfig().VndkVersion() == "current"
 }
 
-func (vendorSnapshotImage) inImage(m *Module) func() bool {
+func (vendorSnapshotImage) inImage(m LinkableInterface) func() bool {
 	return m.InVendor
 }
 
-func (vendorSnapshotImage) private(m *Module) bool {
+func (vendorSnapshotImage) private(m LinkableInterface) bool {
 	return m.IsVndkPrivate()
 }
 
@@ -159,7 +159,7 @@
 	return true
 }
 
-func (vendorSnapshotImage) excludeFromSnapshot(m *Module) bool {
+func (vendorSnapshotImage) excludeFromSnapshot(m LinkableInterface) bool {
 	return m.ExcludeFromVendorSnapshot()
 }
 
@@ -206,12 +206,12 @@
 	return ctx.DeviceConfig().RecoverySnapshotVersion() == "current"
 }
 
-func (recoverySnapshotImage) inImage(m *Module) func() bool {
+func (recoverySnapshotImage) inImage(m LinkableInterface) func() bool {
 	return m.InRecovery
 }
 
 // recovery snapshot does not have private libraries.
-func (recoverySnapshotImage) private(m *Module) bool {
+func (recoverySnapshotImage) private(m LinkableInterface) bool {
 	return false
 }
 
@@ -224,7 +224,7 @@
 	return false
 }
 
-func (recoverySnapshotImage) excludeFromSnapshot(m *Module) bool {
+func (recoverySnapshotImage) excludeFromSnapshot(m LinkableInterface) bool {
 	return m.ExcludeFromRecoverySnapshot()
 }
 
diff --git a/cc/snapshot_utils.go b/cc/snapshot_utils.go
index c32fa36..8eb6164 100644
--- a/cc/snapshot_utils.go
+++ b/cc/snapshot_utils.go
@@ -23,6 +23,36 @@
 	headerExts = []string{".h", ".hh", ".hpp", ".hxx", ".h++", ".inl", ".inc", ".ipp", ".h.generic"}
 )
 
+func (m *Module) IsSnapshotLibrary() bool {
+	if _, ok := m.linker.(snapshotLibraryInterface); ok {
+		return true
+	}
+	return false
+}
+
+func (m *Module) SnapshotHeaders() android.Paths {
+	if m.IsSnapshotLibrary() {
+		return m.linker.(snapshotLibraryInterface).snapshotHeaders()
+	}
+	return android.Paths{}
+}
+
+func (m *Module) Dylib() bool {
+	return false
+}
+
+func (m *Module) Rlib() bool {
+	return false
+}
+
+func (m *Module) SnapshotRuntimeLibs() []string {
+	return m.Properties.SnapshotRuntimeLibs
+}
+
+func (m *Module) SnapshotSharedLibs() []string {
+	return m.Properties.SnapshotSharedLibs
+}
+
 // snapshotLibraryInterface is an interface for libraries captured to VNDK / vendor snapshots.
 type snapshotLibraryInterface interface {
 	libraryInterface
@@ -68,14 +98,14 @@
 	return snapshot, found
 }
 
-// shouldCollectHeadersForSnapshot determines if the module is a possible candidate for snapshot.
+// ShouldCollectHeadersForSnapshot determines if the module is a possible candidate for snapshot.
 // If it's true, collectHeadersForSnapshot will be called in GenerateAndroidBuildActions.
-func shouldCollectHeadersForSnapshot(ctx android.ModuleContext, m *Module, apexInfo android.ApexInfo) bool {
+func ShouldCollectHeadersForSnapshot(ctx android.ModuleContext, m LinkableInterface, apexInfo android.ApexInfo) bool {
 	if ctx.DeviceConfig().VndkVersion() != "current" &&
 		ctx.DeviceConfig().RecoverySnapshotVersion() != "current" {
 		return false
 	}
-	if _, _, ok := isVndkSnapshotAware(ctx.DeviceConfig(), m, apexInfo); ok {
+	if _, ok := isVndkSnapshotAware(ctx.DeviceConfig(), m, apexInfo); ok {
 		return ctx.Config().VndkSnapshotBuildArtifacts()
 	}
 
diff --git a/cc/vendor_snapshot.go b/cc/vendor_snapshot.go
index 9ad51ad..4e59a95 100644
--- a/cc/vendor_snapshot.go
+++ b/cc/vendor_snapshot.go
@@ -115,7 +115,7 @@
 	// still be a vendor proprietary module. This happens for cc modules
 	// that are excluded from the vendor snapshot, and it means that the
 	// vendor has assumed control of the framework-provided module.
-	if c, ok := ctx.Module().(*Module); ok {
+	if c, ok := ctx.Module().(LinkableInterface); ok {
 		if c.ExcludeFromVendorSnapshot() {
 			return true
 		}
@@ -137,7 +137,7 @@
 	// that are excluded from the recovery snapshot, and it means that the
 	// vendor has assumed control of the framework-provided module.
 
-	if c, ok := ctx.Module().(*Module); ok {
+	if c, ok := ctx.Module().(LinkableInterface); ok {
 		if c.ExcludeFromRecoverySnapshot() {
 			return true
 		}
@@ -147,8 +147,8 @@
 }
 
 // Determines if the module is a candidate for snapshot.
-func isSnapshotAware(cfg android.DeviceConfig, m *Module, inProprietaryPath bool, apexInfo android.ApexInfo, image snapshotImage) bool {
-	if !m.Enabled() || m.Properties.HideFromMake {
+func isSnapshotAware(cfg android.DeviceConfig, m LinkableInterface, inProprietaryPath bool, apexInfo android.ApexInfo, image snapshotImage) bool {
+	if !m.Enabled() || m.HiddenFromMake() {
 		return false
 	}
 	// When android/prebuilt.go selects between source and prebuilt, it sets
@@ -177,51 +177,51 @@
 		return false
 	}
 	// skip kernel_headers which always depend on vendor
-	if _, ok := m.linker.(*kernelHeadersDecorator); ok {
+	if m.KernelHeadersDecorator() {
 		return false
 	}
-	// skip LLNDK libraries which are backward compatible
+
 	if m.IsLlndk() {
 		return false
 	}
 
 	// Libraries
-	if l, ok := m.linker.(snapshotLibraryInterface); ok {
-		if m.sanitize != nil {
+	if sanitizable, ok := m.(PlatformSanitizeable); ok && sanitizable.IsSnapshotLibrary() {
+		if sanitizable.SanitizePropDefined() {
 			// scs and hwasan export both sanitized and unsanitized variants for static and header
 			// Always use unsanitized variants of them.
 			for _, t := range []SanitizerType{scs, Hwasan} {
-				if !l.shared() && m.sanitize.isSanitizerEnabled(t) {
+				if !sanitizable.Shared() && sanitizable.IsSanitizerEnabled(t) {
 					return false
 				}
 			}
 			// cfi also exports both variants. But for static, we capture both.
 			// This is because cfi static libraries can't be linked from non-cfi modules,
 			// and vice versa. This isn't the case for scs and hwasan sanitizers.
-			if !l.static() && !l.shared() && m.sanitize.isSanitizerEnabled(cfi) {
+			if !sanitizable.Static() && !sanitizable.Shared() && sanitizable.IsSanitizerEnabled(cfi) {
 				return false
 			}
 		}
-		if l.static() {
-			return m.outputFile.Valid() && !image.private(m)
+		if sanitizable.Static() {
+			return sanitizable.OutputFile().Valid() && !image.private(m)
 		}
-		if l.shared() {
-			if !m.outputFile.Valid() {
+		if sanitizable.Shared() {
+			if !sanitizable.OutputFile().Valid() {
 				return false
 			}
 			if image.includeVndk() {
-				if !m.IsVndk() {
+				if !sanitizable.IsVndk() {
 					return true
 				}
-				return m.IsVndkExt()
+				return sanitizable.IsVndkExt()
 			}
 		}
 		return true
 	}
 
 	// Binaries and Objects
-	if m.binary() || m.object() {
-		return m.outputFile.Valid()
+	if m.Binary() || m.Object() {
+		return m.OutputFile().Valid()
 	}
 
 	return false
@@ -323,7 +323,7 @@
 
 	// installSnapshot function copies prebuilt file (.so, .a, or executable) and json flag file.
 	// For executables, init_rc and vintf_fragments files are also copied.
-	installSnapshot := func(m *Module, fake bool) android.Paths {
+	installSnapshot := func(m LinkableInterface, fake bool) android.Paths {
 		targetArch := "arch-" + m.Target().Arch.ArchType.String()
 		if m.Target().Arch.ArchVariant != "" {
 			targetArch += "-" + m.Target().Arch.ArchVariant
@@ -337,7 +337,7 @@
 		prop.ModuleName = ctx.ModuleName(m)
 		if c.supportsVndkExt && m.IsVndkExt() {
 			// vndk exts are installed to /vendor/lib(64)?/vndk(-sp)?
-			if m.isVndkSp() {
+			if m.IsVndkSp() {
 				prop.RelativeInstallPath = "vndk-sp"
 			} else {
 				prop.RelativeInstallPath = "vndk"
@@ -345,7 +345,7 @@
 		} else {
 			prop.RelativeInstallPath = m.RelativeInstallPath()
 		}
-		prop.RuntimeLibs = m.Properties.SnapshotRuntimeLibs
+		prop.RuntimeLibs = m.SnapshotRuntimeLibs()
 		prop.Required = m.RequiredModuleNames()
 		for _, path := range m.InitRc() {
 			prop.InitRc = append(prop.InitRc, filepath.Join("configs", path.Base()))
@@ -365,8 +365,8 @@
 
 		var propOut string
 
-		if l, ok := m.linker.(snapshotLibraryInterface); ok {
-			exporterInfo := ctx.ModuleProvider(m, FlagExporterInfoProvider).(FlagExporterInfo)
+		if m.IsSnapshotLibrary() {
+			exporterInfo := ctx.ModuleProvider(m.Module(), FlagExporterInfoProvider).(FlagExporterInfo)
 
 			// library flags
 			prop.ExportedFlags = exporterInfo.Flags
@@ -376,19 +376,22 @@
 			for _, dir := range exporterInfo.SystemIncludeDirs {
 				prop.ExportedSystemDirs = append(prop.ExportedSystemDirs, filepath.Join("include", dir.String()))
 			}
+
 			// shared libs dependencies aren't meaningful on static or header libs
-			if l.shared() {
-				prop.SharedLibs = m.Properties.SnapshotSharedLibs
+			if m.Shared() {
+				prop.SharedLibs = m.SnapshotSharedLibs()
 			}
-			if l.static() && m.sanitize != nil {
-				prop.SanitizeMinimalDep = m.sanitize.Properties.MinimalRuntimeDep || enableMinimalRuntime(m.sanitize)
-				prop.SanitizeUbsanDep = m.sanitize.Properties.UbsanRuntimeDep || enableUbsanRuntime(m.sanitize)
+			if sanitizable, ok := m.(PlatformSanitizeable); ok {
+				if sanitizable.Static() && sanitizable.SanitizePropDefined() {
+					prop.SanitizeMinimalDep = sanitizable.MinimalRuntimeDep() || sanitizable.MinimalRuntimeNeeded()
+					prop.SanitizeUbsanDep = sanitizable.UbsanRuntimeDep() || sanitizable.UbsanRuntimeNeeded()
+				}
 			}
 
 			var libType string
-			if l.static() {
+			if m.Static() {
 				libType = "static"
-			} else if l.shared() {
+			} else if m.Shared() {
 				libType = "shared"
 			} else {
 				libType = "header"
@@ -398,16 +401,18 @@
 
 			// install .a or .so
 			if libType != "header" {
-				libPath := m.outputFile.Path()
+				libPath := m.OutputFile().Path()
 				stem = libPath.Base()
-				if l.static() && m.sanitize != nil && m.sanitize.isSanitizerEnabled(cfi) {
-					// both cfi and non-cfi variant for static libraries can exist.
-					// attach .cfi to distinguish between cfi and non-cfi.
-					// e.g. libbase.a -> libbase.cfi.a
-					ext := filepath.Ext(stem)
-					stem = strings.TrimSuffix(stem, ext) + ".cfi" + ext
-					prop.Sanitize = "cfi"
-					prop.ModuleName += ".cfi"
+				if sanitizable, ok := m.(PlatformSanitizeable); ok {
+					if sanitizable.Static() && 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
+						ext := filepath.Ext(stem)
+						stem = strings.TrimSuffix(stem, ext) + ".cfi" + ext
+						prop.Sanitize = "cfi"
+						prop.ModuleName += ".cfi"
+					}
 				}
 				snapshotLibOut := filepath.Join(snapshotArchDir, targetArch, libType, stem)
 				ret = append(ret, copyFile(ctx, libPath, snapshotLibOut, fake))
@@ -416,20 +421,20 @@
 			}
 
 			propOut = filepath.Join(snapshotArchDir, targetArch, libType, stem+".json")
-		} else if m.binary() {
+		} else if m.Binary() {
 			// binary flags
 			prop.Symlinks = m.Symlinks()
-			prop.SharedLibs = m.Properties.SnapshotSharedLibs
+			prop.SharedLibs = m.SnapshotSharedLibs()
 
 			// install bin
-			binPath := m.outputFile.Path()
+			binPath := m.OutputFile().Path()
 			snapshotBinOut := filepath.Join(snapshotArchDir, targetArch, "binary", binPath.Base())
 			ret = append(ret, copyFile(ctx, binPath, snapshotBinOut, fake))
 			propOut = snapshotBinOut + ".json"
-		} else if m.object() {
+		} else if m.Object() {
 			// object files aren't installed to the device, so their names can conflict.
 			// Use module name as stem.
-			objPath := m.outputFile.Path()
+			objPath := m.OutputFile().Path()
 			snapshotObjOut := filepath.Join(snapshotArchDir, targetArch, "object",
 				ctx.ModuleName(m)+filepath.Ext(objPath.Base()))
 			ret = append(ret, copyFile(ctx, objPath, snapshotObjOut, fake))
@@ -450,7 +455,7 @@
 	}
 
 	ctx.VisitAllModules(func(module android.Module) {
-		m, ok := module.(*Module)
+		m, ok := module.(LinkableInterface)
 		if !ok {
 			return
 		}
@@ -484,8 +489,8 @@
 		// installSnapshot installs prebuilts and json flag files
 		snapshotOutputs = append(snapshotOutputs, installSnapshot(m, installAsFake)...)
 		// just gather headers and notice files here, because they are to be deduplicated
-		if l, ok := m.linker.(snapshotLibraryInterface); ok {
-			headers = append(headers, l.snapshotHeaders()...)
+		if m.IsSnapshotLibrary() {
+			headers = append(headers, m.SnapshotHeaders()...)
 		}
 
 		if len(m.NoticeFiles()) > 0 {
diff --git a/cc/vndk.go b/cc/vndk.go
index 8b3788b..0254edc 100644
--- a/cc/vndk.go
+++ b/cc/vndk.go
@@ -372,7 +372,7 @@
 			if mctx.ModuleName() == "libz" {
 				return false
 			}
-			return m.ImageVariation().Variation == android.CoreVariation && lib.shared() && m.isVndkSp()
+			return m.ImageVariation().Variation == android.CoreVariation && lib.shared() && m.IsVndkSp()
 		}
 
 		useCoreVariant := m.VndkVersion() == mctx.DeviceConfig().PlatformVndkVersion() &&
@@ -574,38 +574,37 @@
 	vndkSnapshotZipFile android.OptionalPath
 }
 
-func isVndkSnapshotAware(config android.DeviceConfig, m *Module,
-	apexInfo android.ApexInfo) (i snapshotLibraryInterface, vndkType string, isVndkSnapshotLib bool) {
+func isVndkSnapshotAware(config android.DeviceConfig, m LinkableInterface,
+	apexInfo android.ApexInfo) (vndkType string, isVndkSnapshotLib bool) {
 
 	if m.Target().NativeBridge == android.NativeBridgeEnabled {
-		return nil, "", false
+		return "", false
 	}
 	// !inVendor: There's product/vendor variants for VNDK libs. We only care about vendor variants.
 	// !installable: Snapshot only cares about "installable" modules.
 	// !m.IsLlndk: llndk stubs are required for building against snapshots.
 	// IsSnapshotPrebuilt: Snapshotting a snapshot doesn't make sense.
 	// !outputFile.Valid: Snapshot requires valid output file.
-	if !m.InVendor() || (!m.installable(apexInfo) && !m.IsLlndk()) || m.IsSnapshotPrebuilt() || !m.outputFile.Valid() {
-		return nil, "", false
+	if !m.InVendor() || (!installable(m, apexInfo) && !m.IsLlndk()) || m.IsSnapshotPrebuilt() || !m.OutputFile().Valid() {
+		return "", false
 	}
-	l, ok := m.linker.(snapshotLibraryInterface)
-	if !ok || !l.shared() {
-		return nil, "", false
+	if !m.IsSnapshotLibrary() || !m.Shared() {
+		return "", false
 	}
 	if m.VndkVersion() == config.PlatformVndkVersion() {
 		if m.IsVndk() && !m.IsVndkExt() {
-			if m.isVndkSp() {
-				return l, "vndk-sp", true
+			if m.IsVndkSp() {
+				return "vndk-sp", true
 			} else {
-				return l, "vndk-core", true
+				return "vndk-core", true
 			}
-		} else if l.hasLLNDKStubs() && l.stubsVersion() == "" {
+		} else if m.HasLlndkStubs() && m.StubsVersion() == "" {
 			// Use default version for the snapshot.
-			return l, "llndk-stub", true
+			return "llndk-stub", true
 		}
 	}
 
-	return nil, "", false
+	return "", false
 }
 
 func (c *vndkSnapshotSingleton) GenerateBuildActions(ctx android.SingletonContext) {
@@ -722,7 +721,7 @@
 
 		apexInfo := ctx.ModuleProvider(module, android.ApexInfoProvider).(android.ApexInfo)
 
-		l, vndkType, ok := isVndkSnapshotAware(ctx.DeviceConfig(), m, apexInfo)
+		vndkType, ok := isVndkSnapshotAware(ctx.DeviceConfig(), m, apexInfo)
 		if !ok {
 			return
 		}
@@ -761,7 +760,7 @@
 		}
 
 		if ctx.Config().VndkSnapshotBuildArtifacts() {
-			headers = append(headers, l.snapshotHeaders()...)
+			headers = append(headers, m.SnapshotHeaders()...)
 		}
 	})
 
diff --git a/filesystem/system_image.go b/filesystem/system_image.go
index a7c9143..1d24d6d 100644
--- a/filesystem/system_image.go
+++ b/filesystem/system_image.go
@@ -50,17 +50,19 @@
 func (s *systemImage) buildLinkerConfigFile(ctx android.ModuleContext, root android.OutputPath) android.OutputPath {
 	input := android.PathForModuleSrc(ctx, android.String(s.properties.Linker_config_src))
 	output := root.Join(ctx, "system", "etc", "linker.config.pb")
+
+	// we need "Module"s for packaging items
 	var otherModules []android.Module
+	deps := s.GatherPackagingSpecs(ctx)
 	ctx.WalkDeps(func(child, parent android.Module) bool {
-		// Don't track direct dependencies that aren't not packaged
-		if parent == s {
-			if pi, ok := ctx.OtherModuleDependencyTag(child).(android.PackagingItem); !ok || !pi.IsPackagingItem() {
-				return false
+		for _, ps := range child.PackagingSpecs() {
+			if _, ok := deps[ps.RelPathInPackage()]; ok {
+				otherModules = append(otherModules, child)
 			}
 		}
-		otherModules = append(otherModules, child)
 		return true
 	})
+
 	builder := android.NewRuleBuilder(pctx, ctx)
 	linkerconfig.BuildLinkerConfig(ctx, builder, input, otherModules, output)
 	builder.Build("conv_linker_config", "Generate linker config protobuf "+output.String())
diff --git a/java/app.go b/java/app.go
index 5695022..fc1ace0 100755
--- a/java/app.go
+++ b/java/app.go
@@ -893,7 +893,7 @@
 	return ctx.Device() && ctx.DeviceConfig().NativeCoverageEnabled()
 }
 
-func (a *AndroidApp) PreventInstall() {
+func (a *AndroidApp) SetPreventInstall() {
 	a.appProperties.PreventInstall = true
 }
 
diff --git a/rust/Android.bp b/rust/Android.bp
index f45404f..b611672 100644
--- a/rust/Android.bp
+++ b/rust/Android.bp
@@ -31,8 +31,9 @@
         "protobuf.go",
         "rust.go",
         "sanitize.go",
-        "strip.go",
         "source_provider.go",
+        "snapshot_utils.go",
+        "strip.go",
         "test.go",
         "testing.go",
     ],
diff --git a/rust/compiler.go b/rust/compiler.go
index a3f02c0..1598ebf 100644
--- a/rust/compiler.go
+++ b/rust/compiler.go
@@ -341,6 +341,11 @@
 	return compiler.Properties.Crate_name
 }
 
+func (compiler *baseCompiler) everInstallable() bool {
+	// Most modules are installable, so return true by default.
+	return true
+}
+
 func (compiler *baseCompiler) installDir(ctx ModuleContext) android.InstallPath {
 	dir := compiler.dir
 	if ctx.toolchain().Is64Bit() && compiler.dir64 != "" {
diff --git a/rust/proc_macro.go b/rust/proc_macro.go
index 4eead32..c217959 100644
--- a/rust/proc_macro.go
+++ b/rust/proc_macro.go
@@ -82,3 +82,8 @@
 func (procMacro *procMacroDecorator) autoDep(ctx android.BottomUpMutatorContext) autoDep {
 	return rlibAutoDep
 }
+
+func (procMacro *procMacroDecorator) everInstallable() bool {
+	// Proc_macros are never installed
+	return false
+}
diff --git a/rust/rust.go b/rust/rust.go
index bb97142..f068b3d 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -97,6 +97,7 @@
 
 	PreventInstall bool
 	HideFromMake   bool
+	Installable    *bool
 }
 
 type Module struct {
@@ -143,6 +144,10 @@
 	mod.Properties.HideFromMake = true
 }
 
+func (c *Module) HiddenFromMake() bool {
+	return c.Properties.HideFromMake
+}
+
 func (mod *Module) SanitizePropDefined() bool {
 	// Because compiler is not set for some Rust modules where sanitize might be set, check that compiler is also not
 	// nil since we need compiler to actually sanitize.
@@ -210,6 +215,38 @@
 	return false
 }
 
+func (mod *Module) Dylib() bool {
+	if mod.compiler != nil {
+		if library, ok := mod.compiler.(libraryInterface); ok {
+			return library.dylib()
+		}
+	}
+	return false
+}
+
+func (mod *Module) Rlib() bool {
+	if mod.compiler != nil {
+		if library, ok := mod.compiler.(libraryInterface); ok {
+			return library.rlib()
+		}
+	}
+	return false
+}
+
+func (mod *Module) Binary() bool {
+	if mod.compiler != nil {
+		if _, ok := mod.compiler.(*binaryDecorator); ok {
+			return true
+		}
+	}
+	return false
+}
+
+func (mod *Module) Object() bool {
+	// Rust has no modules which produce only object files.
+	return false
+}
+
 func (mod *Module) Toc() android.OptionalPath {
 	if mod.compiler != nil {
 		if _, ok := mod.compiler.(libraryInterface); ok {
@@ -223,12 +260,13 @@
 	return false
 }
 
-// Returns true if the module is using VNDK libraries instead of the libraries in /system/lib or /system/lib64.
-// "product" and "vendor" variant modules return true for this function.
-// When BOARD_VNDK_VERSION is set, vendor variants of "vendor_available: true", "vendor: true",
-// "soc_specific: true" and more vendor installed modules are included here.
-// When PRODUCT_PRODUCT_VNDK_VERSION is set, product variants of "vendor_available: true" or
-// "product_specific: true" modules are included here.
+func (mod *Module) RelativeInstallPath() string {
+	if mod.compiler != nil {
+		return mod.compiler.relativeInstallPath()
+	}
+	return ""
+}
+
 func (mod *Module) UseVndk() bool {
 	return mod.Properties.VndkVersion != ""
 }
@@ -250,6 +288,10 @@
 	return false
 }
 
+func (mod *Module) IsVndkSp() bool {
+	return false
+}
+
 func (c *Module) IsVndkPrivate() bool {
 	return false
 }
@@ -274,6 +316,14 @@
 	return false
 }
 
+func (mod *Module) HasLlndkStubs() bool {
+	return false
+}
+
+func (mod *Module) StubsVersion() string {
+	panic(fmt.Errorf("StubsVersion called on non-versioned module: %q", mod.BaseModuleName()))
+}
+
 func (mod *Module) SdkVersion() string {
 	return ""
 }
@@ -362,6 +412,7 @@
 	inData() bool
 	install(ctx ModuleContext)
 	relativeInstallPath() string
+	everInstallable() bool
 
 	nativeCoverage() bool
 
@@ -423,8 +474,12 @@
 	return mod.coverage != nil && mod.coverage.Properties.NeedCoverageVariant
 }
 
-func (mod *Module) PreventInstall() {
-	mod.Properties.PreventInstall = true
+func (mod *Module) VndkVersion() string {
+	return mod.Properties.VndkVersion
+}
+
+func (mod *Module) PreventInstall() bool {
+	return mod.Properties.PreventInstall
 }
 
 func (mod *Module) HideFromMake() {
@@ -676,6 +731,16 @@
 	return mod.compiler != nil && mod.compiler.nativeCoverage()
 }
 
+func (mod *Module) EverInstallable() bool {
+	return mod.compiler != nil &&
+		// Check to see whether the module is actually ever installable.
+		mod.compiler.everInstallable()
+}
+
+func (mod *Module) Installable() *bool {
+	return mod.Properties.Installable
+}
+
 func (mod *Module) toolchain(ctx android.BaseModuleContext) config.Toolchain {
 	if mod.cachedToolchain == nil {
 		mod.cachedToolchain = config.FindToolchain(ctx.Os(), ctx.Arch())
diff --git a/rust/sanitize.go b/rust/sanitize.go
index 0a53f98..3d14d51 100644
--- a/rust/sanitize.go
+++ b/rust/sanitize.go
@@ -189,6 +189,22 @@
 	}
 }
 
+func (m *Module) UbsanRuntimeNeeded() bool {
+	return false
+}
+
+func (m *Module) MinimalRuntimeNeeded() bool {
+	return false
+}
+
+func (m *Module) UbsanRuntimeDep() bool {
+	return false
+}
+
+func (m *Module) MinimalRuntimeDep() bool {
+	return false
+}
+
 // Check if the sanitizer is explicitly disabled (as opposed to nil by
 // virtue of not being set).
 func (sanitize *sanitize) isSanitizerExplicitlyDisabled(t cc.SanitizerType) bool {
diff --git a/rust/snapshot_utils.go b/rust/snapshot_utils.go
new file mode 100644
index 0000000..e0ed1f7
--- /dev/null
+++ b/rust/snapshot_utils.go
@@ -0,0 +1,54 @@
+// 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"
+)
+
+func (mod *Module) ExcludeFromVendorSnapshot() bool {
+	// TODO Rust does not yet support snapshotting
+	return true
+}
+
+func (mod *Module) ExcludeFromRecoverySnapshot() bool {
+	// TODO Rust does not yet support snapshotting
+	return true
+}
+
+func (mod *Module) IsSnapshotLibrary() bool {
+	// TODO Rust does not yet support snapshotting
+	return false
+}
+
+func (mod *Module) SnapshotRuntimeLibs() []string {
+	// TODO Rust does not yet support a runtime libs notion similar to CC
+	return []string{}
+}
+
+func (mod *Module) SnapshotSharedLibs() []string {
+	// TODO Rust does not yet support snapshotting
+	return []string{}
+}
+
+func (mod *Module) Symlinks() []string {
+	// TODO update this to return the list of symlinks when Rust supports defining symlinks
+	return nil
+}
+
+func (m *Module) SnapshotHeaders() android.Paths {
+	// TODO Rust does not yet support snapshotting
+	return android.Paths{}
+}
diff --git a/zip/zip.go b/zip/zip.go
index 84e974b..6e412c9 100644
--- a/zip/zip.go
+++ b/zip/zip.go
@@ -656,9 +656,11 @@
 			UncompressedSize64: uint64(fileSize),
 		}
 
+		mode := os.FileMode(0600)
 		if executable {
-			header.SetMode(0700)
+			mode = 0700
 		}
+		header.SetMode(mode)
 
 		err = createParentDirs(dest, src)
 		if err != nil {
diff --git a/zip/zip_test.go b/zip/zip_test.go
index a37ae41..441dea3 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:      0,
+		ExternalAttrs:      (syscall.S_IFREG | 0600) << 16,
 	}
 }