Merge "rust: Switch rust_fuzz to HWASan"
diff --git a/android/Android.bp b/android/Android.bp
index a32e8f2..f5e5606 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -78,9 +78,6 @@
         "variable.go",
         "visibility.go",
         "writedocs.go",
-
-        // Lock down environment access last
-        "env.go",
     ],
     testSrcs: [
         "android_test.go",
diff --git a/android/bazel_handler.go b/android/bazel_handler.go
index 9b44ae3..2697007 100644
--- a/android/bazel_handler.go
+++ b/android/bazel_handler.go
@@ -67,11 +67,7 @@
 
 	// TODO(cparsons): Other cquery-related methods should be added here.
 	// Returns the results of GetOutputFiles and GetCcObjectFiles in a single query (in that order).
-	GetOutputFilesAndCcObjectFiles(label string, archType ArchType) ([]string, []string, bool)
-
-	// GetPrebuiltCcStaticLibraryFiles returns paths to prebuilt cc static libraries, and whether the
-	// results were available
-	GetPrebuiltCcStaticLibraryFiles(label string, archType ArchType) ([]string, bool)
+	GetCcInfo(label string, archType ArchType) (cquery.CcInfo, bool, error)
 
 	// ** End cquery methods
 
@@ -127,9 +123,8 @@
 type MockBazelContext struct {
 	OutputBaseDir string
 
-	LabelToOutputFiles                 map[string][]string
-	LabelToOutputFilesAndCcObjectFiles map[string]cquery.GetOutputFilesAndCcObjectFiles_Result
-	LabelToCcStaticLibraryFiles        map[string][]string
+	LabelToOutputFiles map[string][]string
+	LabelToCcInfo      map[string]cquery.CcInfo
 }
 
 func (m MockBazelContext) GetOutputFiles(label string, archType ArchType) ([]string, bool) {
@@ -137,14 +132,9 @@
 	return result, ok
 }
 
-func (m MockBazelContext) GetOutputFilesAndCcObjectFiles(label string, archType ArchType) ([]string, []string, bool) {
-	result, ok := m.LabelToOutputFilesAndCcObjectFiles[label]
-	return result.OutputFiles, result.CcObjectFiles, ok
-}
-
-func (m MockBazelContext) GetPrebuiltCcStaticLibraryFiles(label string, archType ArchType) ([]string, bool) {
-	result, ok := m.LabelToCcStaticLibraryFiles[label]
-	return result, ok
+func (m MockBazelContext) GetCcInfo(label string, archType ArchType) (cquery.CcInfo, bool, error) {
+	result, ok := m.LabelToCcInfo[label]
+	return result, ok, nil
 }
 
 func (m MockBazelContext) InvokeBazel() error {
@@ -173,39 +163,22 @@
 	return ret, ok
 }
 
-func (bazelCtx *bazelContext) GetOutputFilesAndCcObjectFiles(label string, archType ArchType) ([]string, []string, bool) {
-	var outputFiles []string
-	var ccObjects []string
-
-	result, ok := bazelCtx.cquery(label, cquery.GetOutputFilesAndCcObjectFiles, archType)
-	if ok {
-		bazelOutput := strings.TrimSpace(result)
-		returnResult := cquery.GetOutputFilesAndCcObjectFiles.ParseResult(bazelOutput)
-		outputFiles = returnResult.OutputFiles
-		ccObjects = returnResult.CcObjectFiles
-	}
-
-	return outputFiles, ccObjects, ok
-}
-
-// GetPrebuiltCcStaticLibraryFiles returns a slice of prebuilt static libraries for the given
-// label/archType if there are query results; otherwise, it enqueues the query and returns false.
-func (bazelCtx *bazelContext) GetPrebuiltCcStaticLibraryFiles(label string, archType ArchType) ([]string, bool) {
-	result, ok := bazelCtx.cquery(label, cquery.GetPrebuiltCcStaticLibraryFiles, archType)
+func (bazelCtx *bazelContext) GetCcInfo(label string, archType ArchType) (cquery.CcInfo, bool, error) {
+	result, ok := bazelCtx.cquery(label, cquery.GetCcInfo, archType)
 	if !ok {
-		return nil, false
+		return cquery.CcInfo{}, ok, nil
 	}
 
 	bazelOutput := strings.TrimSpace(result)
-	ret := cquery.GetPrebuiltCcStaticLibraryFiles.ParseResult(bazelOutput)
-	return ret, ok
+	ret, err := cquery.GetCcInfo.ParseResult(bazelOutput)
+	return ret, ok, err
 }
 
 func (n noopBazelContext) GetOutputFiles(label string, archType ArchType) ([]string, bool) {
 	panic("unimplemented")
 }
 
-func (n noopBazelContext) GetOutputFilesAndCcObjectFiles(label string, archType ArchType) ([]string, []string, bool) {
+func (n noopBazelContext) GetCcInfo(label string, archType ArchType) (cquery.CcInfo, bool, error) {
 	panic("unimplemented")
 }
 
diff --git a/android/config.go b/android/config.go
index 80651bb..c566e34 100644
--- a/android/config.go
+++ b/android/config.go
@@ -345,7 +345,7 @@
 // multiple runs in the same program execution is carried over (such as Bazel
 // context or environment deps).
 func ConfigForAdditionalRun(c Config) (Config, error) {
-	newConfig, err := NewConfig(c.srcDir, c.buildDir, c.moduleListFile)
+	newConfig, err := NewConfig(c.srcDir, c.buildDir, c.moduleListFile, c.env)
 	if err != nil {
 		return Config{}, err
 	}
@@ -356,12 +356,12 @@
 
 // NewConfig creates a new Config object. The srcDir argument specifies the path
 // to the root source directory. It also loads the config file, if found.
-func NewConfig(srcDir, buildDir string, moduleListFile string) (Config, error) {
+func NewConfig(srcDir, buildDir string, moduleListFile string, availableEnv map[string]string) (Config, error) {
 	// Make a config with default options.
 	config := &config{
 		ProductVariablesFileName: filepath.Join(buildDir, productVariablesFileName),
 
-		env: originalEnv,
+		env: availableEnv,
 
 		srcDir:            srcDir,
 		buildDir:          buildDir,
diff --git a/android/env.go b/android/env.go
deleted file mode 100644
index 725a145..0000000
--- a/android/env.go
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2015 Google Inc. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package android
-
-import (
-	"android/soong/shared"
-)
-
-// This file supports dependencies on environment variables.  During build
-// manifest generation, any dependency on an environment variable is added to a
-// list.  At the end of the build, a JSON file called soong.environment.used is
-// written containing the current value of all used environment variables. The
-// next time the top-level build script is run, soong_ui parses the compare the
-// contents of the used environment variables, then, if they changed, deletes
-// soong.environment.used to cause a rebuild.
-//
-// The dependency of build.ninja on soong.environment.used is declared in
-// build.ninja.d
-
-var originalEnv map[string]string
-
-func InitEnvironment(envFile string) {
-	var err error
-	originalEnv, err = shared.EnvFromFile(envFile)
-	if err != nil {
-		panic(err)
-	}
-}
diff --git a/android/paths.go b/android/paths.go
index 37b04dd..df12228 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -1101,11 +1101,12 @@
 		// a single file.
 		files, err = gctx.GlobWithDeps(path.String(), nil)
 	} else {
-		var deps []string
+		var result pathtools.GlobResult
 		// We cannot add build statements in this context, so we fall back to
 		// AddNinjaFileDeps
-		files, deps, err = ctx.Config().fs.Glob(path.String(), nil, pathtools.FollowSymlinks)
-		ctx.AddNinjaFileDeps(deps...)
+		result, err = ctx.Config().fs.Glob(path.String(), nil, pathtools.FollowSymlinks)
+		ctx.AddNinjaFileDeps(result.Deps...)
+		files = result.Matches
 	}
 
 	if err != nil {
diff --git a/android/sdk_version.go b/android/sdk_version.go
index 5fdaa91..98db824 100644
--- a/android/sdk_version.go
+++ b/android/sdk_version.go
@@ -22,15 +22,15 @@
 
 type SdkContext interface {
 	// SdkVersion returns SdkSpec that corresponds to the sdk_version property of the current module
-	SdkVersion() SdkSpec
+	SdkVersion(ctx EarlyModuleContext) SdkSpec
 	// SystemModules returns the system_modules property of the current module, or an empty string if it is not set.
 	SystemModules() string
 	// MinSdkVersion returns SdkSpec that corresponds to the min_sdk_version property of the current module,
 	// or from sdk_version if it is not set.
-	MinSdkVersion() SdkSpec
+	MinSdkVersion(ctx EarlyModuleContext) SdkSpec
 	// TargetSdkVersion returns the SdkSpec that corresponds to the target_sdk_version property of the current module,
 	// or from sdk_version if it is not set.
-	TargetSdkVersion() SdkSpec
+	TargetSdkVersion(ctx EarlyModuleContext) SdkSpec
 }
 
 // SdkKind represents a particular category of an SDK spec like public, system, test, etc.
@@ -201,15 +201,23 @@
 	return ctx.Config().DefaultAppTargetSdk(ctx).String(), nil
 }
 
-func SdkSpecFrom(str string) SdkSpec {
+var (
+	SdkSpecNone = SdkSpec{SdkNone, NoneApiLevel, "(no version)"}
+	// TODO(b/175678607) ApiLevel of SdkSpecPrivate should be FutureApiLevel
+	SdkSpecPrivate = SdkSpec{SdkPrivate, NoneApiLevel, ""}
+	// TODO(b/175678607) ApiLevel of SdkSpecCorePlatform should be FutureApiLevel
+	SdkSpecCorePlatform = SdkSpec{SdkCorePlatform, NoneApiLevel, "core_platform"}
+)
+
+func SdkSpecFrom(ctx EarlyModuleContext, str string) SdkSpec {
 	switch str {
 	// special cases first
 	case "":
-		return SdkSpec{SdkPrivate, NoneApiLevel, str}
+		return SdkSpecPrivate
 	case "none":
-		return SdkSpec{SdkNone, NoneApiLevel, str}
+		return SdkSpecNone
 	case "core_platform":
-		return SdkSpec{SdkCorePlatform, NoneApiLevel, str}
+		return SdkSpecCorePlatform
 	default:
 		// the syntax is [kind_]version
 		sep := strings.LastIndex(str, "_")
@@ -242,15 +250,10 @@
 			return SdkSpec{SdkInvalid, NoneApiLevel, str}
 		}
 
-		var apiLevel ApiLevel
-		if versionString == "current" {
-			apiLevel = FutureApiLevel
-		} else if i, err := strconv.Atoi(versionString); err == nil {
-			apiLevel = uncheckedFinalApiLevel(i)
-		} else {
+		apiLevel, err := ApiLevelFromUser(ctx, versionString)
+		if err != nil {
 			return SdkSpec{SdkInvalid, apiLevel, str}
 		}
-
 		return SdkSpec{kind, apiLevel, str}
 	}
 }
diff --git a/apex/apex.go b/apex/apex.go
index 880028f..0eacf6d 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -2272,8 +2272,10 @@
 		tag := ctx.OtherModuleDependencyTag(module)
 		switch tag {
 		case javaLibTag, androidAppTag:
-			if m, ok := module.(interface{ CheckStableSdkVersion() error }); ok {
-				if err := m.CheckStableSdkVersion(); err != nil {
+			if m, ok := module.(interface {
+				CheckStableSdkVersion(ctx android.BaseModuleContext) error
+			}); ok {
+				if err := m.CheckStableSdkVersion(ctx); err != nil {
 					ctx.ModuleErrorf("cannot depend on \"%v\": %v", ctx.OtherModuleName(module), err)
 				}
 			}
diff --git a/apex/apex_test.go b/apex/apex_test.go
index ee4255e..f39c7e3 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -4375,9 +4375,7 @@
 // These tests verify that the prebuilt_apex/deapexer to java_import wiring allows for the
 // propagation of paths to dex implementation jars from the former to the latter.
 func TestPrebuiltExportDexImplementationJars(t *testing.T) {
-	transform := func(config *dexpreopt.GlobalConfig) {
-		// Empty transformation.
-	}
+	transform := android.NullFixturePreparer
 
 	checkDexJarBuildPath := func(t *testing.T, ctx *android.TestContext, name string) {
 		// Make sure the import has been given the correct path to the dex jar.
@@ -4547,9 +4545,7 @@
 }
 
 func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) {
-	transform := func(config *dexpreopt.GlobalConfig) {
-		config.BootJars = android.CreateTestConfiguredJarList([]string{"myapex:libfoo", "myapex:libbar"})
-	}
+	preparer := java.FixtureConfigureBootJars("myapex:libfoo", "myapex:libbar")
 
 	checkBootDexJarPath := func(t *testing.T, ctx *android.TestContext, stem string, bootDexJarPath string) {
 		t.Helper()
@@ -4605,7 +4601,7 @@
 		}
 	`
 
-		ctx := testDexpreoptWithApexes(t, bp, "", transform)
+		ctx := testDexpreoptWithApexes(t, bp, "", preparer)
 		checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar")
 		checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libbar.jar")
 
@@ -4639,7 +4635,7 @@
 		}
 	`
 
-		ctx := testDexpreoptWithApexes(t, bp, "", transform)
+		ctx := testDexpreoptWithApexes(t, bp, "", preparer)
 		checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar")
 		checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libbar.jar")
 
@@ -4698,7 +4694,7 @@
 		// prebuilt_apex module always depends on the prebuilt, and so it doesn't
 		// find the dex boot jar in it. We either need to disable the source libfoo
 		// or make the prebuilt libfoo preferred.
-		testDexpreoptWithApexes(t, bp, "failed to find a dex jar path for module 'libfoo'", transform)
+		testDexpreoptWithApexes(t, bp, "failed to find a dex jar path for module 'libfoo'", preparer)
 	})
 
 	t.Run("prebuilt library preferred with source", func(t *testing.T) {
@@ -4746,7 +4742,7 @@
 		}
 	`
 
-		ctx := testDexpreoptWithApexes(t, bp, "", transform)
+		ctx := testDexpreoptWithApexes(t, bp, "", preparer)
 		checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar")
 		checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libbar.jar")
 
@@ -4813,7 +4809,7 @@
 		}
 	`
 
-		ctx := testDexpreoptWithApexes(t, bp, "", transform)
+		ctx := testDexpreoptWithApexes(t, bp, "", preparer)
 		checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/libfoo/android_common_apex10000/hiddenapi/libfoo.jar")
 		checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/libbar/android_common_myapex/hiddenapi/libbar.jar")
 
@@ -4830,7 +4826,7 @@
 			name: "myapex",
 			enabled: false,
 			key: "myapex.key",
-			java_libs: ["libfoo"],
+			java_libs: ["libfoo", "libbar"],
 		}
 
 		apex_key {
@@ -4882,14 +4878,14 @@
 		}
 	`
 
-		ctx := testDexpreoptWithApexes(t, bp, "", transform)
+		ctx := testDexpreoptWithApexes(t, bp, "", preparer)
 		checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar")
 		checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libbar.jar")
 
 		// Make sure that the dex file from the prebuilt_apex contributes to the hiddenapi index file.
 		checkHiddenAPIIndexInputs(t, ctx, `
-.intermediates/prebuilt_libbar/android_common_prebuilt_myapex/hiddenapi/index.csv
-.intermediates/prebuilt_libfoo/android_common_prebuilt_myapex/hiddenapi/index.csv
+.intermediates/prebuilt_libbar/android_common_myapex/hiddenapi/index.csv
+.intermediates/prebuilt_libfoo/android_common_myapex/hiddenapi/index.csv
 `)
 	})
 }
@@ -6440,7 +6436,7 @@
 	android.AssertStringEquals(t, "myapex input", extractorOutput, copiedApex.Input.String())
 }
 
-func testNoUpdatableJarsInBootImage(t *testing.T, errmsg string, transformDexpreoptConfig func(*dexpreopt.GlobalConfig)) {
+func testNoUpdatableJarsInBootImage(t *testing.T, errmsg string, preparer android.FixturePreparer) {
 	t.Helper()
 
 	bp := `
@@ -6528,10 +6524,10 @@
 		}
 	`
 
-	testDexpreoptWithApexes(t, bp, errmsg, transformDexpreoptConfig)
+	testDexpreoptWithApexes(t, bp, errmsg, preparer)
 }
 
-func testDexpreoptWithApexes(t *testing.T, bp, errmsg string, transformDexpreoptConfig func(*dexpreopt.GlobalConfig)) *android.TestContext {
+func testDexpreoptWithApexes(t *testing.T, bp, errmsg string, preparer android.FixturePreparer) *android.TestContext {
 	t.Helper()
 
 	fs := android.MockFS{
@@ -6557,18 +6553,13 @@
 		java.PrepareForTestWithJavaDefaultModules,
 		java.PrepareForTestWithJavaSdkLibraryFiles,
 		PrepareForTestWithApexBuildComponents,
-		android.FixtureModifyConfig(func(config android.Config) {
-			pathCtx := android.PathContextForTesting(config)
-			dexpreoptConfig := dexpreopt.GlobalConfigForTests(pathCtx)
-			transformDexpreoptConfig(dexpreoptConfig)
-			dexpreopt.SetTestGlobalConfig(config, dexpreoptConfig)
-
-			// Make sure that any changes to these dexpreopt properties are mirrored in the corresponding
-			// product variables.
-			config.TestProductVariables.BootJars = dexpreoptConfig.BootJars
-			config.TestProductVariables.UpdatableBootJars = dexpreoptConfig.UpdatableBootJars
-		}),
+		preparer,
 		fs.AddToFixture(),
+		android.FixtureAddTextFile("frameworks/base/boot/Android.bp", `
+			platform_bootclasspath {
+				name: "platform-bootclasspath",
+			}
+		`),
 	).
 		ExtendWithErrorHandler(errorHandler).
 		RunTestWithBp(t, bp)
@@ -6608,92 +6599,95 @@
 }
 
 func TestNoUpdatableJarsInBootImage(t *testing.T) {
-	var err string
-	var transform func(*dexpreopt.GlobalConfig)
+	// Set the BootJars in dexpreopt.GlobalConfig and productVariables to the same value. This can
+	// result in an invalid configuration as it does not set the ArtApexJars and allows art apex
+	// modules to be included in the BootJars.
+	prepareSetBootJars := func(bootJars ...string) android.FixturePreparer {
+		return android.GroupFixturePreparers(
+			dexpreopt.FixtureSetBootJars(bootJars...),
+			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+				variables.BootJars = android.CreateTestConfiguredJarList(bootJars)
+			}),
+		)
+	}
+
+	// Set the ArtApexJars and BootJars in dexpreopt.GlobalConfig and productVariables all to the
+	// same value. This can result in an invalid configuration as it allows non art apex jars to be
+	// specified in the ArtApexJars configuration.
+	prepareSetArtJars := func(bootJars ...string) android.FixturePreparer {
+		return android.GroupFixturePreparers(
+			dexpreopt.FixtureSetArtBootJars(bootJars...),
+			dexpreopt.FixtureSetBootJars(bootJars...),
+			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+				variables.BootJars = android.CreateTestConfiguredJarList(bootJars)
+			}),
+		)
+	}
 
 	t.Run("updatable jar from ART apex in the ART boot image => ok", func(t *testing.T) {
-		transform = func(config *dexpreopt.GlobalConfig) {
-			config.ArtApexJars = android.CreateTestConfiguredJarList([]string{"com.android.art.debug:some-art-lib"})
-		}
-		testNoUpdatableJarsInBootImage(t, "", transform)
+		preparer := java.FixtureConfigureBootJars("com.android.art.debug:some-art-lib")
+		testNoUpdatableJarsInBootImage(t, "", preparer)
 	})
 
 	t.Run("updatable jar from ART apex in the framework boot image => error", func(t *testing.T) {
-		err = `module "some-art-lib" from updatable apexes \["com.android.art.debug"\] is not allowed in the framework boot image`
-		transform = func(config *dexpreopt.GlobalConfig) {
-			config.BootJars = android.CreateTestConfiguredJarList([]string{"com.android.art.debug:some-art-lib"})
-		}
-		testNoUpdatableJarsInBootImage(t, err, transform)
+		err := `module "some-art-lib" from updatable apexes \["com.android.art.debug"\] is not allowed in the framework boot image`
+		// Update the dexpreopt BootJars directly.
+		preparer := prepareSetBootJars("com.android.art.debug:some-art-lib")
+		testNoUpdatableJarsInBootImage(t, err, preparer)
 	})
 
 	t.Run("updatable jar from some other apex in the ART boot image => error", func(t *testing.T) {
-		err = `module "some-updatable-apex-lib" from updatable apexes \["some-updatable-apex"\] is not allowed in the ART boot image`
-		transform = func(config *dexpreopt.GlobalConfig) {
-			config.ArtApexJars = android.CreateTestConfiguredJarList([]string{"some-updatable-apex:some-updatable-apex-lib"})
-		}
-		testNoUpdatableJarsInBootImage(t, err, transform)
+		err := `module "some-updatable-apex-lib" from updatable apexes \["some-updatable-apex"\] is not allowed in the ART boot image`
+		// Update the dexpreopt ArtApexJars directly.
+		preparer := prepareSetArtJars("some-updatable-apex:some-updatable-apex-lib")
+		testNoUpdatableJarsInBootImage(t, err, preparer)
 	})
 
 	t.Run("non-updatable jar from some other apex in the ART boot image => error", func(t *testing.T) {
-		err = `module "some-non-updatable-apex-lib" is not allowed in the ART boot image`
-		transform = func(config *dexpreopt.GlobalConfig) {
-			config.ArtApexJars = android.CreateTestConfiguredJarList([]string{"some-non-updatable-apex:some-non-updatable-apex-lib"})
-		}
-		testNoUpdatableJarsInBootImage(t, err, transform)
+		err := `module "some-non-updatable-apex-lib" is not allowed in the ART boot image`
+		// Update the dexpreopt ArtApexJars directly.
+		preparer := prepareSetArtJars("some-non-updatable-apex:some-non-updatable-apex-lib")
+		testNoUpdatableJarsInBootImage(t, err, preparer)
 	})
 
 	t.Run("updatable jar from some other apex in the framework boot image => error", func(t *testing.T) {
-		err = `module "some-updatable-apex-lib" from updatable apexes \["some-updatable-apex"\] is not allowed in the framework boot image`
-		transform = func(config *dexpreopt.GlobalConfig) {
-			config.BootJars = android.CreateTestConfiguredJarList([]string{"some-updatable-apex:some-updatable-apex-lib"})
-		}
-		testNoUpdatableJarsInBootImage(t, err, transform)
+		err := `module "some-updatable-apex-lib" from updatable apexes \["some-updatable-apex"\] is not allowed in the framework boot image`
+		preparer := java.FixtureConfigureBootJars("some-updatable-apex:some-updatable-apex-lib")
+		testNoUpdatableJarsInBootImage(t, err, preparer)
 	})
 
 	t.Run("non-updatable jar from some other apex in the framework boot image => ok", func(t *testing.T) {
-		transform = func(config *dexpreopt.GlobalConfig) {
-			config.BootJars = android.CreateTestConfiguredJarList([]string{"some-non-updatable-apex:some-non-updatable-apex-lib"})
-		}
-		testNoUpdatableJarsInBootImage(t, "", transform)
+		preparer := java.FixtureConfigureBootJars("some-non-updatable-apex:some-non-updatable-apex-lib")
+		testNoUpdatableJarsInBootImage(t, "", preparer)
 	})
 
 	t.Run("nonexistent jar in the ART boot image => error", func(t *testing.T) {
-		err = "failed to find a dex jar path for module 'nonexistent'"
-		transform = func(config *dexpreopt.GlobalConfig) {
-			config.ArtApexJars = android.CreateTestConfiguredJarList([]string{"platform:nonexistent"})
-		}
-		testNoUpdatableJarsInBootImage(t, err, transform)
+		err := `"platform-bootclasspath" depends on undefined module "nonexistent"`
+		preparer := java.FixtureConfigureBootJars("platform:nonexistent")
+		testNoUpdatableJarsInBootImage(t, err, preparer)
 	})
 
 	t.Run("nonexistent jar in the framework boot image => error", func(t *testing.T) {
-		err = "failed to find a dex jar path for module 'nonexistent'"
-		transform = func(config *dexpreopt.GlobalConfig) {
-			config.BootJars = android.CreateTestConfiguredJarList([]string{"platform:nonexistent"})
-		}
-		testNoUpdatableJarsInBootImage(t, err, transform)
+		err := `"platform-bootclasspath" depends on undefined module "nonexistent"`
+		preparer := java.FixtureConfigureBootJars("platform:nonexistent")
+		testNoUpdatableJarsInBootImage(t, err, preparer)
 	})
 
 	t.Run("platform jar in the ART boot image => error", func(t *testing.T) {
-		err = `module "some-platform-lib" is not allowed in the ART boot image`
-		transform = func(config *dexpreopt.GlobalConfig) {
-			config.ArtApexJars = android.CreateTestConfiguredJarList([]string{"platform:some-platform-lib"})
-		}
-		testNoUpdatableJarsInBootImage(t, err, transform)
+		err := `module "some-platform-lib" is not allowed in the ART boot image`
+		// Update the dexpreopt ArtApexJars directly.
+		preparer := prepareSetArtJars("platform:some-platform-lib")
+		testNoUpdatableJarsInBootImage(t, err, preparer)
 	})
 
 	t.Run("platform jar in the framework boot image => ok", func(t *testing.T) {
-		transform = func(config *dexpreopt.GlobalConfig) {
-			config.BootJars = android.CreateTestConfiguredJarList([]string{"platform:some-platform-lib"})
-		}
-		testNoUpdatableJarsInBootImage(t, "", transform)
+		preparer := java.FixtureConfigureBootJars("platform:some-platform-lib")
+		testNoUpdatableJarsInBootImage(t, "", preparer)
 	})
-
 }
 
 func TestDexpreoptAccessDexFilesFromPrebuiltApex(t *testing.T) {
-	transform := func(config *dexpreopt.GlobalConfig) {
-		config.BootJars = android.CreateTestConfiguredJarList([]string{"myapex:libfoo"})
-	}
+	preparer := java.FixtureConfigureBootJars("myapex:libfoo")
 	t.Run("prebuilt no source", func(t *testing.T) {
 		testDexpreoptWithApexes(t, `
 			prebuilt_apex {
@@ -6713,7 +6707,7 @@
 			name: "libfoo",
 			jars: ["libfoo.jar"],
 		}
-`, "", transform)
+`, "", preparer)
 	})
 
 	t.Run("prebuilt no source", func(t *testing.T) {
@@ -6735,7 +6729,7 @@
 			name: "libfoo",
 			jars: ["libfoo.jar"],
 		}
-`, "", transform)
+`, "", preparer)
 	})
 }
 
diff --git a/apex/boot_image_test.go b/apex/boot_image_test.go
index aa0d9c4..d447d70 100644
--- a/apex/boot_image_test.go
+++ b/apex/boot_image_test.go
@@ -19,7 +19,6 @@
 	"testing"
 
 	"android/soong/android"
-	"android/soong/dexpreopt"
 	"android/soong/java"
 )
 
@@ -42,8 +41,7 @@
 	result := android.GroupFixturePreparers(
 		prepareForTestWithBootImage,
 		// Configure some libraries in the art and framework boot images.
-		dexpreopt.FixtureSetArtBootJars("com.android.art:baz", "com.android.art:quuz"),
-		dexpreopt.FixtureSetBootJars("platform:foo", "platform:bar"),
+		java.FixtureConfigureBootJars("com.android.art:baz", "com.android.art:quuz", "platform:foo", "platform:bar"),
 		prepareForTestWithArtApex,
 
 		java.PrepareForTestWithJavaSdkLibraryFiles,
@@ -169,7 +167,7 @@
 		prepareForTestWithArtApex,
 
 		// Configure some libraries in the art boot image.
-		dexpreopt.FixtureSetArtBootJars("com.android.art:foo", "com.android.art:bar"),
+		java.FixtureConfigureBootJars("com.android.art:foo", "com.android.art:bar"),
 	).RunTestWithBp(t, `
 		apex {
 			name: "com.android.art",
@@ -264,7 +262,7 @@
 		}),
 
 		// Configure some libraries in the art boot image.
-		dexpreopt.FixtureSetArtBootJars("com.android.art:foo", "com.android.art:bar"),
+		java.FixtureConfigureBootJars("com.android.art:foo", "com.android.art:bar"),
 	).RunTestWithBp(t, `
 		prebuilt_apex {
 			name: "com.android.art",
diff --git a/apex/builder.go b/apex/builder.go
index 2df380b..bbb4666 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -946,16 +946,19 @@
 			depInfos[to.Name()] = info
 		} else {
 			toMinSdkVersion := "(no version)"
-			if m, ok := to.(interface{ MinSdkVersion() string }); ok {
+			if m, ok := to.(interface {
+				MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec
+			}); ok {
+				if v := m.MinSdkVersion(ctx); !v.ApiLevel.IsNone() {
+					toMinSdkVersion = v.ApiLevel.String()
+				}
+			} else if m, ok := to.(interface{ MinSdkVersion() string }); ok {
+				// TODO(b/175678607) eliminate the use of MinSdkVersion returning
+				// string
 				if v := m.MinSdkVersion(); v != "" {
 					toMinSdkVersion = v
 				}
-			} else if m, ok := to.(interface{ MinSdkVersionString() string }); ok {
-				if v := m.MinSdkVersionString(); v != "" {
-					toMinSdkVersion = v
-				}
 			}
-
 			depInfos[to.Name()] = android.ApexModuleDepInfo{
 				To:            to.Name(),
 				From:          []string{from.Name()},
diff --git a/apex/platform_bootclasspath_test.go b/apex/platform_bootclasspath_test.go
index 2ea6401..74830d3 100644
--- a/apex/platform_bootclasspath_test.go
+++ b/apex/platform_bootclasspath_test.go
@@ -18,7 +18,6 @@
 	"testing"
 
 	"android/soong/android"
-	"android/soong/dexpreopt"
 	"android/soong/java"
 	"github.com/google/blueprint"
 )
@@ -37,9 +36,8 @@
 		prepareForTestWithArtApex,
 		prepareForTestWithMyapex,
 		// Configure some libraries in the art and framework boot images.
-		dexpreopt.FixtureSetArtBootJars("com.android.art:baz", "com.android.art:quuz"),
-		dexpreopt.FixtureSetBootJars("platform:foo"),
-		dexpreopt.FixtureSetUpdatableBootJars("myapex:bar"),
+		java.FixtureConfigureBootJars("com.android.art:baz", "com.android.art:quuz", "platform:foo"),
+		java.FixtureConfigureUpdatableBootJars("myapex:bar"),
 		java.PrepareForTestWithJavaSdkLibraryFiles,
 		java.FixtureWithLastReleaseApis("foo"),
 	).RunTestWithBp(t, `
diff --git a/apex/prebuilt.go b/apex/prebuilt.go
index 10a70a3..a9d24a7 100644
--- a/apex/prebuilt.go
+++ b/apex/prebuilt.go
@@ -172,7 +172,7 @@
 
 	// Create an ApexInfo for the prebuilt_apex.
 	apexInfo := android.ApexInfo{
-		ApexVariationName: mctx.ModuleName(),
+		ApexVariationName: android.RemoveOptionalPrebuiltPrefix(mctx.ModuleName()),
 		InApexes:          []string{mctx.ModuleName()},
 		ApexContents:      []*android.ApexContents{apexContents},
 		ForPrebuiltApex:   true,
diff --git a/bazel/cquery/request_type.go b/bazel/cquery/request_type.go
index b88da2f..8049108 100644
--- a/bazel/cquery/request_type.go
+++ b/bazel/cquery/request_type.go
@@ -1,18 +1,19 @@
 package cquery
 
 import (
+	"fmt"
 	"strings"
 )
 
 var (
-	GetOutputFiles                  = &getOutputFilesRequestType{}
-	GetOutputFilesAndCcObjectFiles  = &getOutputFilesAndCcObjectFilesType{}
-	GetPrebuiltCcStaticLibraryFiles = &getPrebuiltCcStaticLibraryFiles{}
+	GetOutputFiles = &getOutputFilesRequestType{}
+	GetCcInfo      = &getCcInfoType{}
 )
 
-type GetOutputFilesAndCcObjectFiles_Result struct {
-	OutputFiles   []string
-	CcObjectFiles []string
+type CcInfo struct {
+	OutputFiles          []string
+	CcObjectFiles        []string
+	CcStaticLibraryFiles []string
 }
 
 type getOutputFilesRequestType struct{}
@@ -39,15 +40,15 @@
 // The given rawString must correspond to the string output which was created by evaluating the
 // Starlark given in StarlarkFunctionBody.
 func (g getOutputFilesRequestType) ParseResult(rawString string) []string {
-	return strings.Split(rawString, ", ")
+	return splitOrEmpty(rawString, ", ")
 }
 
-type getOutputFilesAndCcObjectFilesType struct{}
+type getCcInfoType struct{}
 
 // Name returns a string name for this request type. Such request type names must be unique,
 // and must only consist of alphanumeric characters.
-func (g getOutputFilesAndCcObjectFilesType) Name() string {
-	return "getOutputFilesAndCcObjectFiles"
+func (g getCcInfoType) Name() string {
+	return "getCcInfo"
 }
 
 // StarlarkFunctionBody returns a starlark function body to process this request type.
@@ -58,61 +59,52 @@
 //   - `target` is the only parameter to this function (a configured target).
 //   - The return value must be a string.
 //   - The function body should not be indented outside of its own scope.
-func (g getOutputFilesAndCcObjectFilesType) StarlarkFunctionBody() string {
+func (g getCcInfoType) StarlarkFunctionBody() string {
 	return `
 outputFiles = [f.path for f in target.files.to_list()]
 
 ccObjectFiles = []
+staticLibraries = []
 linker_inputs = providers(target)["CcInfo"].linking_context.linker_inputs.to_list()
 
 for linker_input in linker_inputs:
   for library in linker_input.libraries:
     for object in library.objects:
       ccObjectFiles += [object.path]
-return ', '.join(outputFiles) + "|" + ', '.join(ccObjectFiles)`
+    if library.static_library:
+      staticLibraries.append(library.static_library.path)
+
+returns = [
+  outputFiles,
+  staticLibraries,
+  ccObjectFiles,
+]
+
+return "|".join([", ".join(r) for r in returns])`
 }
 
 // ParseResult returns a value obtained by parsing the result of the request's Starlark function.
 // The given rawString must correspond to the string output which was created by evaluating the
 // Starlark given in StarlarkFunctionBody.
-func (g getOutputFilesAndCcObjectFilesType) ParseResult(rawString string) GetOutputFilesAndCcObjectFiles_Result {
+func (g getCcInfoType) ParseResult(rawString string) (CcInfo, error) {
 	var outputFiles []string
 	var ccObjects []string
 
 	splitString := strings.Split(rawString, "|")
+	if expectedLen := 3; len(splitString) != expectedLen {
+		return CcInfo{}, fmt.Errorf("Expected %d items, got %q", expectedLen, splitString)
+	}
 	outputFilesString := splitString[0]
-	ccObjectsString := splitString[1]
+	ccStaticLibrariesString := splitString[1]
+	ccObjectsString := splitString[2]
 	outputFiles = splitOrEmpty(outputFilesString, ", ")
+	ccStaticLibraries := splitOrEmpty(ccStaticLibrariesString, ", ")
 	ccObjects = splitOrEmpty(ccObjectsString, ", ")
-	return GetOutputFilesAndCcObjectFiles_Result{outputFiles, ccObjects}
-}
-
-type getPrebuiltCcStaticLibraryFiles struct{}
-
-// Name returns the name of the starlark function to get prebuilt cc static library files
-func (g getPrebuiltCcStaticLibraryFiles) Name() string {
-	return "getPrebuiltCcStaticLibraryFiles"
-}
-
-// StarlarkFunctionBody returns the unindented body of a starlark function for extracting the static
-// library paths from a cc_import module.
-func (g getPrebuiltCcStaticLibraryFiles) StarlarkFunctionBody() string {
-	return `
-linker_inputs = providers(target)["CcInfo"].linking_context.linker_inputs.to_list()
-
-static_libraries = []
-for linker_input in linker_inputs:
-  for library in linker_input.libraries:
-    static_libraries.append(library.static_library.path)
-
-return ', '.join(static_libraries)`
-}
-
-// ParseResult returns a slice of bazel output paths to static libraries if any exist for the given
-// rawString corresponding to the string output which was created by evaluating the
-// StarlarkFunctionBody.
-func (g getPrebuiltCcStaticLibraryFiles) ParseResult(rawString string) []string {
-	return strings.Split(rawString, ", ")
+	return CcInfo{
+		OutputFiles:          outputFiles,
+		CcObjectFiles:        ccObjects,
+		CcStaticLibraryFiles: ccStaticLibraries,
+	}, nil
 }
 
 // splitOrEmpty is a modification of strings.Split() that returns an empty list
diff --git a/bazel/cquery/request_type_test.go b/bazel/cquery/request_type_test.go
new file mode 100644
index 0000000..56e03e2
--- /dev/null
+++ b/bazel/cquery/request_type_test.go
@@ -0,0 +1,89 @@
+package cquery
+
+import (
+	"fmt"
+	"reflect"
+	"testing"
+)
+
+func TestGetOutputFilesParseResults(t *testing.T) {
+	testCases := []struct {
+		description    string
+		input          string
+		expectedOutput []string
+	}{
+		{
+			description:    "no result",
+			input:          "",
+			expectedOutput: []string{},
+		},
+		{
+			description:    "one result",
+			input:          "test",
+			expectedOutput: []string{"test"},
+		},
+		{
+			description:    "splits on comma with space",
+			input:          "foo, bar",
+			expectedOutput: []string{"foo", "bar"},
+		},
+	}
+	for _, tc := range testCases {
+		actualOutput := GetOutputFiles.ParseResult(tc.input)
+		if !reflect.DeepEqual(tc.expectedOutput, actualOutput) {
+			t.Errorf("%q: expected %#v != actual %#v", tc.description, tc.expectedOutput, actualOutput)
+		}
+	}
+}
+
+func TestGetCcInfoParseResults(t *testing.T) {
+	testCases := []struct {
+		description          string
+		input                string
+		expectedOutput       CcInfo
+		expectedErrorMessage string
+	}{
+		{
+			description: "no result",
+			input:       "||",
+			expectedOutput: CcInfo{
+				OutputFiles:          []string{},
+				CcObjectFiles:        []string{},
+				CcStaticLibraryFiles: []string{},
+			},
+		},
+		{
+			description: "only output",
+			input:       "test||",
+			expectedOutput: CcInfo{
+				OutputFiles:          []string{"test"},
+				CcObjectFiles:        []string{},
+				CcStaticLibraryFiles: []string{},
+			},
+		},
+		{
+			description: "all items set",
+			input:       "out1, out2|static_lib1, static_lib2|object1, object2",
+			expectedOutput: CcInfo{
+				OutputFiles:          []string{"out1", "out2"},
+				CcObjectFiles:        []string{"object1", "object2"},
+				CcStaticLibraryFiles: []string{"static_lib1", "static_lib2"},
+			},
+		},
+		{
+			description:          "too few result splits",
+			input:                "|",
+			expectedOutput:       CcInfo{},
+			expectedErrorMessage: fmt.Sprintf("Expected %d items, got %q", 3, []string{"", ""}),
+		},
+	}
+	for _, tc := range testCases {
+		actualOutput, err := GetCcInfo.ParseResult(tc.input)
+		if (err == nil && tc.expectedErrorMessage != "") ||
+			(err != nil && err.Error() != tc.expectedErrorMessage) {
+			t.Errorf("%q: expected Error %s, got %s", tc.description, tc.expectedErrorMessage, err)
+		} else if err == nil && !reflect.DeepEqual(tc.expectedOutput, actualOutput) {
+			t.Errorf("%q: expected %#v != actual %#v", tc.description, tc.expectedOutput, actualOutput)
+		}
+	}
+}
diff --git a/cc/library.go b/cc/library.go
index 738b45f..53be3a5 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -483,10 +483,16 @@
 
 func (handler *staticLibraryBazelHandler) generateBazelBuildActions(ctx android.ModuleContext, label string) bool {
 	bazelCtx := ctx.Config().BazelContext
-	outputPaths, objPaths, ok := bazelCtx.GetOutputFilesAndCcObjectFiles(label, ctx.Arch().ArchType)
+	ccInfo, ok, err := bazelCtx.GetCcInfo(label, ctx.Arch().ArchType)
+	if err != nil {
+		ctx.ModuleErrorf("Error getting Bazel CcInfo: %s", err)
+		return false
+	}
 	if !ok {
 		return ok
 	}
+	outputPaths := ccInfo.OutputFiles
+	objPaths := ccInfo.CcObjectFiles
 	if len(outputPaths) > 1 {
 		// TODO(cparsons): This is actually expected behavior for static libraries with no srcs.
 		// We should support this.
diff --git a/cc/prebuilt.go b/cc/prebuilt.go
index 3829b1e..c19b1ff 100644
--- a/cc/prebuilt.go
+++ b/cc/prebuilt.go
@@ -329,10 +329,14 @@
 
 func (h *prebuiltStaticLibraryBazelHandler) generateBazelBuildActions(ctx android.ModuleContext, label string) bool {
 	bazelCtx := ctx.Config().BazelContext
-	staticLibs, ok := bazelCtx.GetPrebuiltCcStaticLibraryFiles(label, ctx.Arch().ArchType)
+	ccInfo, ok, err := bazelCtx.GetCcInfo(label, ctx.Arch().ArchType)
+	if err != nil {
+		ctx.ModuleErrorf("Error getting Bazel CcInfo: %s", err)
+	}
 	if !ok {
 		return false
 	}
+	staticLibs := ccInfo.CcStaticLibraryFiles
 	if len(staticLibs) > 1 {
 		ctx.ModuleErrorf("expected 1 static library from bazel target %q, got %s", label, staticLibs)
 		return false
diff --git a/cc/snapshot_prebuilt.go b/cc/snapshot_prebuilt.go
index af05102..aa70768 100644
--- a/cc/snapshot_prebuilt.go
+++ b/cc/snapshot_prebuilt.go
@@ -559,10 +559,18 @@
 		return nil
 	}
 
+	// Flags specified directly to this module.
 	p.libraryDecorator.reexportDirs(android.PathsForModuleSrc(ctx, p.properties.Export_include_dirs)...)
 	p.libraryDecorator.reexportSystemDirs(android.PathsForModuleSrc(ctx, p.properties.Export_system_include_dirs)...)
 	p.libraryDecorator.reexportFlags(p.properties.Export_flags...)
 
+	// Flags reexported from dependencies. (e.g. vndk_prebuilt_shared)
+	p.libraryDecorator.reexportDirs(deps.ReexportedDirs...)
+	p.libraryDecorator.reexportSystemDirs(deps.ReexportedSystemDirs...)
+	p.libraryDecorator.reexportFlags(deps.ReexportedFlags...)
+	p.libraryDecorator.reexportDeps(deps.ReexportedDeps...)
+	p.libraryDecorator.addExportedGeneratedHeaders(deps.ReexportedGeneratedHeaders...)
+
 	in := android.PathForModuleSrc(ctx, *p.properties.Src)
 	p.unstrippedOutputFile = in
 
diff --git a/cc/vendor_snapshot_test.go b/cc/vendor_snapshot_test.go
index 1c3f1b4..66396f7 100644
--- a/cc/vendor_snapshot_test.go
+++ b/cc/vendor_snapshot_test.go
@@ -424,6 +424,20 @@
 		srcs: ["client.cpp"],
 	}
 
+	cc_library_shared {
+		name: "libclient_cfi",
+		vendor: true,
+		nocrt: true,
+		no_libcrt: true,
+		stl: "none",
+		system_shared_libs: [],
+		static_libs: ["libvendor"],
+		sanitize: {
+			cfi: true,
+		},
+		srcs: ["client.cpp"],
+	}
+
 	cc_binary {
 		name: "bin_without_snapshot",
 		vendor: true,
@@ -492,13 +506,13 @@
 		arch: {
 			arm64: {
 				src: "libvndk.a",
-				export_include_dirs: ["include/libvndk"],
 			},
 			arm: {
 				src: "libvndk.a",
-				export_include_dirs: ["include/libvndk"],
 			},
 		},
+		shared_libs: ["libvndk"],
+		export_shared_lib_headers: ["libvndk"],
 	}
 
 	vendor_snapshot_shared {
@@ -584,10 +598,18 @@
 		vendor: true,
 		arch: {
 			arm64: {
+				cfi: {
+					src: "libvendor.cfi.a",
+					export_include_dirs: ["include/libvendor_cfi"],
+				},
 				src: "libvendor.a",
 				export_include_dirs: ["include/libvendor"],
 			},
 			arm: {
+				cfi: {
+					src: "libvendor.cfi.a",
+					export_include_dirs: ["include/libvendor_cfi"],
+				},
 				src: "libvendor.a",
 				export_include_dirs: ["include/libvendor"],
 			},
@@ -726,29 +748,31 @@
 	depsBp := GatherRequiredDepsForTest(android.Android)
 
 	mockFS := map[string][]byte{
-		"deps/Android.bp":              []byte(depsBp),
-		"framework/Android.bp":         []byte(frameworkBp),
-		"framework/symbol.txt":         nil,
-		"vendor/Android.bp":            []byte(vendorProprietaryBp),
-		"vendor/bin":                   nil,
-		"vendor/bin32":                 nil,
-		"vendor/bin.cpp":               nil,
-		"vendor/client.cpp":            nil,
-		"vendor/include/libvndk/a.h":   nil,
-		"vendor/include/libvendor/b.h": nil,
-		"vendor/libc++_static.a":       nil,
-		"vendor/libc++demangle.a":      nil,
-		"vendor/libgcc_striped.a":      nil,
-		"vendor/libvndk.a":             nil,
-		"vendor/libvendor.a":           nil,
-		"vendor/libvendor.so":          nil,
-		"vendor/lib32.a":               nil,
-		"vendor/lib32.so":              nil,
-		"vendor/lib64.a":               nil,
-		"vendor/lib64.so":              nil,
-		"vndk/Android.bp":              []byte(vndkBp),
-		"vndk/include/libvndk/a.h":     nil,
-		"vndk/libvndk.so":              nil,
+		"deps/Android.bp":                  []byte(depsBp),
+		"framework/Android.bp":             []byte(frameworkBp),
+		"framework/symbol.txt":             nil,
+		"vendor/Android.bp":                []byte(vendorProprietaryBp),
+		"vendor/bin":                       nil,
+		"vendor/bin32":                     nil,
+		"vendor/bin.cpp":                   nil,
+		"vendor/client.cpp":                nil,
+		"vendor/include/libvndk/a.h":       nil,
+		"vendor/include/libvendor/b.h":     nil,
+		"vendor/include/libvendor_cfi/c.h": nil,
+		"vendor/libc++_static.a":           nil,
+		"vendor/libc++demangle.a":          nil,
+		"vendor/libgcc_striped.a":          nil,
+		"vendor/libvndk.a":                 nil,
+		"vendor/libvendor.a":               nil,
+		"vendor/libvendor.cfi.a":           nil,
+		"vendor/libvendor.so":              nil,
+		"vendor/lib32.a":                   nil,
+		"vendor/lib32.so":                  nil,
+		"vendor/lib64.a":                   nil,
+		"vendor/lib64.so":                  nil,
+		"vndk/Android.bp":                  []byte(vndkBp),
+		"vndk/include/libvndk/a.h":         nil,
+		"vndk/libvndk.so":                  nil,
 	}
 
 	config := TestConfig(t.TempDir(), android.Android, nil, "", mockFS)
@@ -766,6 +790,9 @@
 	staticVariant := "android_vendor.30_arm64_armv8-a_static"
 	binaryVariant := "android_vendor.30_arm64_armv8-a"
 
+	sharedCfiVariant := "android_vendor.30_arm64_armv8-a_shared_cfi"
+	staticCfiVariant := "android_vendor.30_arm64_armv8-a_static_cfi"
+
 	shared32Variant := "android_vendor.30_arm_armv7-a-neon_shared"
 	binary32Variant := "android_vendor.30_arm_armv7-a-neon"
 
@@ -808,9 +835,22 @@
 		t.Errorf("wanted libclient32 AndroidMkSharedLibs %q, got %q", w, g)
 	}
 
-	// bin_without_snapshot uses libvndk.vendor_static.30.arm64
+	// libclient_cfi uses libvendor.vendor_static.30.arm64's cfi variant
+	libclientCfiCcFlags := ctx.ModuleForTests("libclient_cfi", sharedCfiVariant).Rule("cc").Args["cFlags"]
+	if !strings.Contains(libclientCfiCcFlags, "-Ivendor/include/libvendor_cfi") {
+		t.Errorf("flags for libclient_cfi must contain %#v, but was %#v.",
+			"-Ivendor/include/libvendor_cfi", libclientCfiCcFlags)
+	}
+
+	libclientCfiLdFlags := ctx.ModuleForTests("libclient_cfi", sharedCfiVariant).Rule("ld").Args["libFlags"]
+	libvendorCfiOutputPaths := getOutputPaths(ctx, staticCfiVariant, []string{"libvendor.vendor_static.30.arm64"})
+	if !strings.Contains(libclientCfiLdFlags, libvendorCfiOutputPaths[0].String()) {
+		t.Errorf("libflags for libclientCfi must contain %#v, but was %#v", libvendorCfiOutputPaths[0], libclientCfiLdFlags)
+	}
+
+	// bin_without_snapshot uses libvndk.vendor_static.30.arm64 (which reexports vndk's exported headers)
 	binWithoutSnapshotCcFlags := ctx.ModuleForTests("bin_without_snapshot", binaryVariant).Rule("cc").Args["cFlags"]
-	if !strings.Contains(binWithoutSnapshotCcFlags, "-Ivendor/include/libvndk") {
+	if !strings.Contains(binWithoutSnapshotCcFlags, "-Ivndk/include/libvndk") {
 		t.Errorf("flags for bin_without_snapshot must contain %#v, but was %#v.",
 			"-Ivendor/include/libvndk", binWithoutSnapshotCcFlags)
 	}
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index 431d621..a4554fc 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -75,8 +75,8 @@
 	return ctx
 }
 
-func newConfig(srcDir, outDir string) android.Config {
-	configuration, err := android.NewConfig(srcDir, outDir, bootstrap.CmdlineModuleListFile())
+func newConfig(srcDir, outDir string, availableEnv map[string]string) android.Config {
+	configuration, err := android.NewConfig(srcDir, outDir, bootstrap.CmdlineModuleListFile(), availableEnv)
 	if err != nil {
 		fmt.Fprintf(os.Stderr, "%s", err)
 		os.Exit(1)
@@ -188,12 +188,31 @@
 
 	shared.ReexecWithDelveMaybe(delveListen, delvePath)
 	android.InitSandbox(topDir)
-	android.InitEnvironment(shared.JoinPath(topDir, outDir, "soong.environment.available"))
 
-	usedVariablesFile := shared.JoinPath(outDir, "soong.environment.used")
+	// soong_ui dumps the available environment variables to
+	// soong.environment.available . Then soong_build itself is run with an empty
+	// environment so that the only way environment variables can be accessed is
+	// using Config, which tracks access to them.
+
+	// At the end of the build, a file called soong.environment.used is written
+	// containing the current value of all used environment variables. The next
+	// time soong_ui is run, it checks whether any environment variables that was
+	// used had changed and if so, it deletes soong.environment.used to cause a
+	// rebuild.
+	//
+	// The dependency of build.ninja on soong.environment.used is declared in
+	// build.ninja.d
+	availableEnvFile := shared.JoinPath(topDir, outDir, "soong.environment.available")
+	usedEnvFile := shared.JoinPath(topDir, outDir, "soong.environment.used")
+	availableEnv, err := shared.EnvFromFile(availableEnvFile)
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "error reading available environment file %s: %s\n", availableEnvFile, err)
+		os.Exit(1)
+	}
+
 	// The top-level Blueprints file is passed as the first argument.
 	srcDir := filepath.Dir(flag.Arg(0))
-	configuration := newConfig(srcDir, outDir)
+	configuration := newConfig(srcDir, outDir, availableEnv)
 	extraNinjaDeps := []string{
 		configuration.ProductVariablesFileName,
 		shared.JoinPath(outDir, "soong.environment.used"),
@@ -219,19 +238,19 @@
 	}
 
 	doChosenActivity(configuration, extraNinjaDeps)
-	writeUsedVariablesFile(shared.JoinPath(topDir, usedVariablesFile), configuration)
+	writeUsedEnvironmentFile(usedEnvFile, configuration)
 }
 
-func writeUsedVariablesFile(path string, configuration android.Config) {
+func writeUsedEnvironmentFile(path string, configuration android.Config) {
 	data, err := shared.EnvFileContents(configuration.EnvDeps())
 	if err != nil {
-		fmt.Fprintf(os.Stderr, "error writing used variables file %s: %s\n", path, err)
+		fmt.Fprintf(os.Stderr, "error writing used environment file %s: %s\n", path, err)
 		os.Exit(1)
 	}
 
 	err = ioutil.WriteFile(path, data, 0666)
 	if err != nil {
-		fmt.Fprintf(os.Stderr, "error writing used variables file %s: %s\n", path, err)
+		fmt.Fprintf(os.Stderr, "error writing used environment file %s: %s\n", path, err)
 		os.Exit(1)
 	}
 
diff --git a/java/aar.go b/java/aar.go
index a122a94..04727e4 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -218,7 +218,7 @@
 	linkDeps = append(linkDeps, assetDeps...)
 
 	// SDK version flags
-	minSdkVersion, err := sdkContext.MinSdkVersion().EffectiveVersionString(ctx)
+	minSdkVersion, err := sdkContext.MinSdkVersion(ctx).EffectiveVersionString(ctx)
 	if err != nil {
 		ctx.ModuleErrorf("invalid minSdkVersion: %s", err)
 	}
@@ -609,6 +609,9 @@
 	hideApexVariantFromMake bool
 
 	aarPath android.Path
+
+	sdkVersion    android.SdkSpec
+	minSdkVersion android.SdkSpec
 }
 
 var _ android.OutputFileProducer = (*AARImport)(nil)
@@ -625,23 +628,23 @@
 	}
 }
 
-func (a *AARImport) SdkVersion() android.SdkSpec {
-	return android.SdkSpecFrom(String(a.properties.Sdk_version))
+func (a *AARImport) SdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
+	return android.SdkSpecFrom(ctx, String(a.properties.Sdk_version))
 }
 
 func (a *AARImport) SystemModules() string {
 	return ""
 }
 
-func (a *AARImport) MinSdkVersion() android.SdkSpec {
+func (a *AARImport) MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
 	if a.properties.Min_sdk_version != nil {
-		return android.SdkSpecFrom(*a.properties.Min_sdk_version)
+		return android.SdkSpecFrom(ctx, *a.properties.Min_sdk_version)
 	}
-	return a.SdkVersion()
+	return a.SdkVersion(ctx)
 }
 
-func (a *AARImport) TargetSdkVersion() android.SdkSpec {
-	return a.SdkVersion()
+func (a *AARImport) TargetSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
+	return a.SdkVersion(ctx)
 }
 
 func (a *AARImport) javaVersion() string {
@@ -727,6 +730,9 @@
 		return
 	}
 
+	a.sdkVersion = a.SdkVersion(ctx)
+	a.minSdkVersion = a.MinSdkVersion(ctx)
+
 	a.hideApexVariantFromMake = !ctx.Provider(android.ApexInfoProvider).(android.ApexInfo).IsForPlatform()
 
 	aarName := ctx.ModuleName() + ".aar"
diff --git a/java/android_manifest.go b/java/android_manifest.go
index 8510f30..331f941 100644
--- a/java/android_manifest.go
+++ b/java/android_manifest.go
@@ -51,7 +51,7 @@
 	if isLibrary {
 		args = append(args, "--library")
 	} else {
-		minSdkVersion, err := sdkContext.MinSdkVersion().EffectiveVersion(ctx)
+		minSdkVersion, err := sdkContext.MinSdkVersion(ctx).EffectiveVersion(ctx)
 		if err != nil {
 			ctx.ModuleErrorf("invalid minSdkVersion: %s", err)
 		}
@@ -87,7 +87,7 @@
 		args = append(args, "--logging-parent", loggingParent)
 	}
 	var deps android.Paths
-	targetSdkVersion, err := sdkContext.TargetSdkVersion().EffectiveVersionString(ctx)
+	targetSdkVersion, err := sdkContext.TargetSdkVersion(ctx).EffectiveVersionString(ctx)
 	if err != nil {
 		ctx.ModuleErrorf("invalid targetSdkVersion: %s", err)
 	}
@@ -96,7 +96,7 @@
 		deps = append(deps, ApiFingerprintPath(ctx))
 	}
 
-	minSdkVersion, err := sdkContext.MinSdkVersion().EffectiveVersionString(ctx)
+	minSdkVersion, err := sdkContext.MinSdkVersion(ctx).EffectiveVersionString(ctx)
 	if err != nil {
 		ctx.ModuleErrorf("invalid minSdkVersion: %s", err)
 	}
diff --git a/java/androidmk.go b/java/androidmk.go
index 75661a7..4e594a2 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -106,7 +106,7 @@
 					if len(library.dexpreopter.builtInstalled) > 0 {
 						entries.SetString("LOCAL_SOONG_BUILT_INSTALLED", library.dexpreopter.builtInstalled)
 					}
-					entries.SetString("LOCAL_SDK_VERSION", library.SdkVersion().Raw)
+					entries.SetString("LOCAL_SDK_VERSION", library.sdkVersion.String())
 					entries.SetPath("LOCAL_SOONG_CLASSES_JAR", library.implementationAndResourcesJar)
 					entries.SetPath("LOCAL_SOONG_HEADER_JAR", library.headerJarFile)
 
@@ -205,7 +205,7 @@
 				}
 				entries.SetPath("LOCAL_SOONG_HEADER_JAR", prebuilt.combinedClasspathFile)
 				entries.SetPath("LOCAL_SOONG_CLASSES_JAR", prebuilt.combinedClasspathFile)
-				entries.SetString("LOCAL_SDK_VERSION", prebuilt.makeSdkVersion())
+				entries.SetString("LOCAL_SDK_VERSION", prebuilt.sdkVersion.String())
 				entries.SetString("LOCAL_MODULE_STEM", prebuilt.Stem())
 			},
 		},
@@ -255,7 +255,7 @@
 				entries.SetPath("LOCAL_SOONG_EXPORT_PROGUARD_FLAGS", prebuilt.proguardFlags)
 				entries.SetPath("LOCAL_SOONG_STATIC_LIBRARY_EXTRA_PACKAGES", prebuilt.extraAaptPackagesFile)
 				entries.SetPath("LOCAL_FULL_MANIFEST_FILE", prebuilt.manifest)
-				entries.SetString("LOCAL_SDK_VERSION", prebuilt.SdkVersion().Raw)
+				entries.SetString("LOCAL_SDK_VERSION", prebuilt.sdkVersion.String())
 			},
 		},
 	}}
diff --git a/java/app.go b/java/app.go
index 04406e7..5695022 100755
--- a/java/app.go
+++ b/java/app.go
@@ -213,7 +213,7 @@
 func (a *AndroidApp) DepsMutator(ctx android.BottomUpMutatorContext) {
 	a.Module.deps(ctx)
 
-	if String(a.appProperties.Stl) == "c++_shared" && !a.SdkVersion().Specified() {
+	if String(a.appProperties.Stl) == "c++_shared" && !a.SdkVersion(ctx).Specified() {
 		ctx.PropertyErrorf("stl", "sdk_version must be set in order to use c++_shared")
 	}
 
@@ -222,7 +222,7 @@
 		a.aapt.deps(ctx, sdkDep)
 	}
 
-	usesSDK := a.SdkVersion().Specified() && a.SdkVersion().Kind != android.SdkCorePlatform
+	usesSDK := a.SdkVersion(ctx).Specified() && a.SdkVersion(ctx).Kind != android.SdkCorePlatform
 
 	if usesSDK && Bool(a.appProperties.Jni_uses_sdk_apis) {
 		ctx.PropertyErrorf("jni_uses_sdk_apis",
@@ -279,14 +279,14 @@
 
 func (a *AndroidApp) checkAppSdkVersions(ctx android.ModuleContext) {
 	if a.Updatable() {
-		if !a.SdkVersion().Stable() {
-			ctx.PropertyErrorf("sdk_version", "Updatable apps must use stable SDKs, found %v", a.SdkVersion())
+		if !a.SdkVersion(ctx).Stable() {
+			ctx.PropertyErrorf("sdk_version", "Updatable apps must use stable SDKs, found %v", a.SdkVersion(ctx))
 		}
 		if String(a.deviceProperties.Min_sdk_version) == "" {
 			ctx.PropertyErrorf("updatable", "updatable apps must set min_sdk_version.")
 		}
 
-		if minSdkVersion, err := a.MinSdkVersion().EffectiveVersion(ctx); err == nil {
+		if minSdkVersion, err := a.MinSdkVersion(ctx).EffectiveVersion(ctx); err == nil {
 			a.checkJniLibsSdkVersion(ctx, minSdkVersion)
 			android.CheckMinSdkVersion(a, ctx, minSdkVersion)
 		} else {
@@ -314,7 +314,7 @@
 		// The domain of cc.sdk_version is "current" and <number>
 		// We can rely on android.SdkSpec to convert it to <number> so that "current" is
 		// handled properly regardless of sdk finalization.
-		jniSdkVersion, err := android.SdkSpecFrom(dep.SdkVersion()).EffectiveVersion(ctx)
+		jniSdkVersion, err := android.SdkSpecFrom(ctx, dep.SdkVersion()).EffectiveVersion(ctx)
 		if err != nil || minSdkVersion.LessThan(jniSdkVersion) {
 			ctx.OtherModuleErrorf(dep, "sdk_version(%v) is higher than min_sdk_version(%v) of the containing android_app(%v)",
 				dep.SdkVersion(), minSdkVersion, ctx.ModuleName())
@@ -327,9 +327,9 @@
 // Returns true if the native libraries should be stored in the APK uncompressed and the
 // extractNativeLibs application flag should be set to false in the manifest.
 func (a *AndroidApp) useEmbeddedNativeLibs(ctx android.ModuleContext) bool {
-	minSdkVersion, err := a.MinSdkVersion().EffectiveVersion(ctx)
+	minSdkVersion, err := a.MinSdkVersion(ctx).EffectiveVersion(ctx)
 	if err != nil {
-		ctx.PropertyErrorf("min_sdk_version", "invalid value %q: %s", a.MinSdkVersion(), err)
+		ctx.PropertyErrorf("min_sdk_version", "invalid value %q: %s", a.MinSdkVersion(ctx), err)
 	}
 
 	apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
@@ -381,7 +381,7 @@
 
 func (a *AndroidApp) aaptBuildActions(ctx android.ModuleContext) {
 	usePlatformAPI := proptools.Bool(a.Module.deviceProperties.Platform_apis)
-	if ctx.Module().(android.SdkContext).SdkVersion().Kind == android.SdkModule {
+	if ctx.Module().(android.SdkContext).SdkVersion(ctx).Kind == android.SdkModule {
 		usePlatformAPI = true
 	}
 	a.aapt.usesNonSdkApis = usePlatformAPI
@@ -724,8 +724,8 @@
 }
 
 type appDepsInterface interface {
-	SdkVersion() android.SdkSpec
-	MinSdkVersion() android.SdkSpec
+	SdkVersion(ctx android.EarlyModuleContext) android.SdkSpec
+	MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec
 	RequiresStableAPIs(ctx android.BaseModuleContext) bool
 }
 
@@ -738,8 +738,8 @@
 	seenModulePaths := make(map[string]bool)
 
 	if checkNativeSdkVersion {
-		checkNativeSdkVersion = app.SdkVersion().Specified() &&
-			app.SdkVersion().Kind != android.SdkCorePlatform && !app.RequiresStableAPIs(ctx)
+		checkNativeSdkVersion = app.SdkVersion(ctx).Specified() &&
+			app.SdkVersion(ctx).Kind != android.SdkCorePlatform && !app.RequiresStableAPIs(ctx)
 	}
 
 	ctx.WalkDeps(func(module android.Module, parent android.Module) bool {
@@ -829,12 +829,16 @@
 			depsInfo[depName] = info
 		} else {
 			toMinSdkVersion := "(no version)"
-			if m, ok := to.(interface{ MinSdkVersion() string }); ok {
-				if v := m.MinSdkVersion(); v != "" {
-					toMinSdkVersion = v
+			if m, ok := to.(interface {
+				MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec
+			}); ok {
+				if v := m.MinSdkVersion(ctx); !v.ApiLevel.IsNone() {
+					toMinSdkVersion = v.ApiLevel.String()
 				}
-			} else if m, ok := to.(interface{ MinSdkVersionString() string }); ok {
-				if v := m.MinSdkVersionString(); v != "" {
+			} else if m, ok := to.(interface{ MinSdkVersion() string }); ok {
+				// TODO(b/175678607) eliminate the use of MinSdkVersion returning
+				// string
+				if v := m.MinSdkVersion(); v != "" {
 					toMinSdkVersion = v
 				}
 			}
@@ -848,7 +852,7 @@
 		return true
 	})
 
-	a.ApexBundleDepsInfo.BuildDepsInfoLists(ctx, a.MinSdkVersionString(), depsInfo)
+	a.ApexBundleDepsInfo.BuildDepsInfoLists(ctx, a.MinSdkVersion(ctx).String(), depsInfo)
 }
 
 func (a *AndroidApp) Updatable() bool {
diff --git a/java/app_import.go b/java/app_import.go
index 32cec23..839051e 100644
--- a/java/app_import.go
+++ b/java/app_import.go
@@ -394,12 +394,12 @@
 	return false
 }
 
-func (a *AndroidAppImport) SdkVersion() android.SdkSpec {
-	return android.SdkSpecFrom("")
+func (a *AndroidAppImport) SdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
+	return android.SdkSpecPrivate
 }
 
-func (a *AndroidAppImport) MinSdkVersion() android.SdkSpec {
-	return android.SdkSpecFrom("")
+func (a *AndroidAppImport) MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
+	return android.SdkSpecPrivate
 }
 
 var _ android.ApexModule = (*AndroidAppImport)(nil)
diff --git a/java/base.go b/java/base.go
index 9bc0738..19c85cd 100644
--- a/java/base.go
+++ b/java/base.go
@@ -370,10 +370,13 @@
 	modulePaths []string
 
 	hideApexVariantFromMake bool
+
+	sdkVersion    android.SdkSpec
+	minSdkVersion android.SdkSpec
 }
 
-func (j *Module) CheckStableSdkVersion() error {
-	sdkVersion := j.SdkVersion()
+func (j *Module) CheckStableSdkVersion(ctx android.BaseModuleContext) error {
+	sdkVersion := j.SdkVersion(ctx)
 	if sdkVersion.Stable() {
 		return nil
 	}
@@ -393,7 +396,7 @@
 func (j *Module) checkSdkVersions(ctx android.ModuleContext) {
 	if j.RequiresStableAPIs(ctx) {
 		if sc, ok := ctx.Module().(android.SdkContext); ok {
-			if !sc.SdkVersion().Specified() {
+			if !sc.SdkVersion(ctx).Specified() {
 				ctx.PropertyErrorf("sdk_version",
 					"sdk_version must have a value when the module is located at vendor or product(only if PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE is set).")
 			}
@@ -418,7 +421,7 @@
 func (j *Module) checkPlatformAPI(ctx android.ModuleContext) {
 	if sc, ok := ctx.Module().(android.SdkContext); ok {
 		usePlatformAPI := proptools.Bool(j.deviceProperties.Platform_apis)
-		sdkVersionSpecified := sc.SdkVersion().Specified()
+		sdkVersionSpecified := sc.SdkVersion(ctx).Specified()
 		if usePlatformAPI && sdkVersionSpecified {
 			ctx.PropertyErrorf("platform_apis", "platform_apis must be false when sdk_version is not empty.")
 		} else if !usePlatformAPI && !sdkVersionSpecified {
@@ -512,30 +515,30 @@
 	return false
 }
 
-func (j *Module) SdkVersion() android.SdkSpec {
-	return android.SdkSpecFrom(String(j.deviceProperties.Sdk_version))
+func (j *Module) SdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
+	return android.SdkSpecFrom(ctx, String(j.deviceProperties.Sdk_version))
 }
 
 func (j *Module) SystemModules() string {
 	return proptools.String(j.deviceProperties.System_modules)
 }
 
-func (j *Module) MinSdkVersion() android.SdkSpec {
+func (j *Module) MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
 	if j.deviceProperties.Min_sdk_version != nil {
-		return android.SdkSpecFrom(*j.deviceProperties.Min_sdk_version)
+		return android.SdkSpecFrom(ctx, *j.deviceProperties.Min_sdk_version)
 	}
-	return j.SdkVersion()
-}
-
-func (j *Module) TargetSdkVersion() android.SdkSpec {
-	if j.deviceProperties.Target_sdk_version != nil {
-		return android.SdkSpecFrom(*j.deviceProperties.Target_sdk_version)
-	}
-	return j.SdkVersion()
+	return j.SdkVersion(ctx)
 }
 
 func (j *Module) MinSdkVersionString() string {
-	return j.MinSdkVersion().ApiLevel.String()
+	return j.minSdkVersion.Raw
+}
+
+func (j *Module) TargetSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
+	if j.deviceProperties.Target_sdk_version != nil {
+		return android.SdkSpecFrom(ctx, *j.deviceProperties.Target_sdk_version)
+	}
+	return j.SdkVersion(ctx)
 }
 
 func (j *Module) AvailableFor(what string) bool {
@@ -1209,7 +1212,7 @@
 			}
 			// Dex compilation
 			var dexOutputFile android.OutputPath
-			dexOutputFile = j.dexer.compileDex(ctx, flags, j.MinSdkVersion(), outputFile, jarName)
+			dexOutputFile = j.dexer.compileDex(ctx, flags, j.MinSdkVersion(ctx), outputFile, jarName)
 			if ctx.Failed() {
 				return
 			}
@@ -1267,9 +1270,9 @@
 		j.linter.srcJars = srcJars
 		j.linter.classpath = append(append(android.Paths(nil), flags.bootClasspath...), flags.classpath...)
 		j.linter.classes = j.implementationJarFile
-		j.linter.minSdkVersion = lintSDKVersionString(j.MinSdkVersion())
-		j.linter.targetSdkVersion = lintSDKVersionString(j.TargetSdkVersion())
-		j.linter.compileSdkVersion = lintSDKVersionString(j.SdkVersion())
+		j.linter.minSdkVersion = lintSDKVersionString(j.MinSdkVersion(ctx))
+		j.linter.targetSdkVersion = lintSDKVersionString(j.TargetSdkVersion(ctx))
+		j.linter.compileSdkVersion = lintSDKVersionString(j.SdkVersion(ctx))
 		j.linter.javaLanguageLevel = flags.javaVersion.String()
 		j.linter.kotlinLanguageLevel = "1.3"
 		if !apexInfo.IsForPlatform() && ctx.Config().UnbundledBuildApps() {
@@ -1471,7 +1474,7 @@
 // Implements android.ApexModule
 func (j *Module) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
 	sdkVersion android.ApiLevel) error {
-	sdkSpec := j.MinSdkVersion()
+	sdkSpec := j.MinSdkVersion(ctx)
 	if !sdkSpec.Specified() {
 		return fmt.Errorf("min_sdk_version is not specified")
 	}
@@ -1551,10 +1554,10 @@
 
 type moduleWithSdkDep interface {
 	android.Module
-	getSdkLinkType(name string) (ret sdkLinkType, stubs bool)
+	getSdkLinkType(ctx android.BaseModuleContext, name string) (ret sdkLinkType, stubs bool)
 }
 
-func (m *Module) getSdkLinkType(name string) (ret sdkLinkType, stubs bool) {
+func (m *Module) getSdkLinkType(ctx android.BaseModuleContext, name string) (ret sdkLinkType, stubs bool) {
 	switch name {
 	case "core.current.stubs", "legacy.core.platform.api.stubs", "stable.core.platform.api.stubs",
 		"stub-annotations", "private-stub-annotations-jar",
@@ -1576,7 +1579,7 @@
 		return linkType, true
 	}
 
-	ver := m.SdkVersion()
+	ver := m.SdkVersion(ctx)
 	switch ver.Kind {
 	case android.SdkCore:
 		return javaCore, false
@@ -1606,11 +1609,11 @@
 		return
 	}
 
-	myLinkType, stubs := j.getSdkLinkType(ctx.ModuleName())
+	myLinkType, stubs := j.getSdkLinkType(ctx, ctx.ModuleName())
 	if stubs {
 		return
 	}
-	depLinkType, _ := dep.getSdkLinkType(ctx.OtherModuleName(dep))
+	depLinkType, _ := dep.getSdkLinkType(ctx, ctx.OtherModuleName(dep))
 
 	if myLinkType.rank() < depLinkType.rank() {
 		ctx.ModuleErrorf("compiles against %v, but dependency %q is compiling against %v. "+
@@ -1638,7 +1641,7 @@
 		}
 	}
 
-	sdkLinkType, _ := j.getSdkLinkType(ctx.ModuleName())
+	sdkLinkType, _ := j.getSdkLinkType(ctx, ctx.ModuleName())
 
 	ctx.VisitDirectDeps(func(module android.Module) {
 		otherName := ctx.OtherModuleName(module)
@@ -1656,7 +1659,7 @@
 		if dep, ok := module.(SdkLibraryDependency); ok {
 			switch tag {
 			case libTag:
-				deps.classpath = append(deps.classpath, dep.SdkHeaderJars(ctx, j.SdkVersion())...)
+				deps.classpath = append(deps.classpath, dep.SdkHeaderJars(ctx, j.SdkVersion(ctx))...)
 			case staticLibTag:
 				ctx.ModuleErrorf("dependency on java_sdk_library %q can only be in libs", otherName)
 			}
diff --git a/java/droiddoc.go b/java/droiddoc.go
index e527d59..01c0f16 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -261,20 +261,20 @@
 
 var _ android.OutputFileProducer = (*Javadoc)(nil)
 
-func (j *Javadoc) SdkVersion() android.SdkSpec {
-	return android.SdkSpecFrom(String(j.properties.Sdk_version))
+func (j *Javadoc) SdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
+	return android.SdkSpecFrom(ctx, String(j.properties.Sdk_version))
 }
 
 func (j *Javadoc) SystemModules() string {
 	return proptools.String(j.properties.System_modules)
 }
 
-func (j *Javadoc) MinSdkVersion() android.SdkSpec {
-	return j.SdkVersion()
+func (j *Javadoc) MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
+	return j.SdkVersion(ctx)
 }
 
-func (j *Javadoc) TargetSdkVersion() android.SdkSpec {
-	return j.SdkVersion()
+func (j *Javadoc) TargetSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
+	return j.SdkVersion(ctx)
 }
 
 func (j *Javadoc) addDeps(ctx android.BottomUpMutatorContext) {
@@ -386,7 +386,7 @@
 			}
 		case libTag:
 			if dep, ok := module.(SdkLibraryDependency); ok {
-				deps.classpath = append(deps.classpath, dep.SdkHeaderJars(ctx, j.SdkVersion())...)
+				deps.classpath = append(deps.classpath, dep.SdkHeaderJars(ctx, j.SdkVersion(ctx))...)
 			} else if ctx.OtherModuleHasProvider(module, JavaInfoProvider) {
 				dep := ctx.OtherModuleProvider(module, JavaInfoProvider).(JavaInfo)
 				deps.classpath = append(deps.classpath, dep.HeaderJars...)
diff --git a/java/hiddenapi.go b/java/hiddenapi.go
index 208ced7..3ecb977 100644
--- a/java/hiddenapi.go
+++ b/java/hiddenapi.go
@@ -111,6 +111,13 @@
 
 var _ hiddenAPIIntf = (*hiddenAPI)(nil)
 
+// hiddenAPISupportingModule is the interface that is implemented by any module that supports
+// contributing to the hidden API processing.
+type hiddenAPISupportingModule interface {
+	android.Module
+	hiddenAPIIntf
+}
+
 // Initialize the hiddenapi structure
 func (h *hiddenAPI) initHiddenAPI(ctx android.BaseModuleContext, configurationName string) {
 	// If hiddenapi processing is disabled treat this as inactive.
diff --git a/java/hiddenapi_singleton_test.go b/java/hiddenapi_singleton_test.go
index 5c449e5..e5e1c25 100644
--- a/java/hiddenapi_singleton_test.go
+++ b/java/hiddenapi_singleton_test.go
@@ -23,12 +23,6 @@
 	"github.com/google/blueprint/proptools"
 )
 
-func fixtureSetBootJarsProductVariable(bootJars ...string) android.FixturePreparer {
-	return android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-		variables.BootJars = android.CreateTestConfiguredJarList(bootJars)
-	})
-}
-
 func fixtureSetPrebuiltHiddenApiDirProductVariable(prebuiltHiddenApiDir *string) android.FixturePreparer {
 	return android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
 		variables.PrebuiltHiddenApiDir = prebuiltHiddenApiDir
@@ -41,7 +35,7 @@
 func TestHiddenAPISingleton(t *testing.T) {
 	result := android.GroupFixturePreparers(
 		hiddenApiFixtureFactory,
-		fixtureSetBootJarsProductVariable("platform:foo"),
+		FixtureConfigureBootJars("platform:foo"),
 	).RunTestWithBp(t, `
 		java_library {
 			name: "foo",
@@ -61,7 +55,7 @@
 		hiddenApiFixtureFactory,
 		PrepareForTestWithJavaSdkLibraryFiles,
 		FixtureWithLastReleaseApis("bar"),
-		fixtureSetBootJarsProductVariable("platform:foo", "platform:bar"),
+		FixtureConfigureBootJars("platform:foo", "platform:bar"),
 	).RunTestWithBp(t, `
 		java_library {
 			name: "foo",
@@ -119,7 +113,7 @@
 
 	android.GroupFixturePreparers(
 		hiddenApiFixtureFactory,
-		fixtureSetBootJarsProductVariable("platform:foo"),
+		FixtureConfigureBootJars("platform:foo"),
 	).ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(expectedErrorMessage)).
 		RunTestWithBp(t, `
 		java_library {
@@ -139,7 +133,7 @@
 func TestHiddenAPISingletonWithPrebuilt(t *testing.T) {
 	result := android.GroupFixturePreparers(
 		hiddenApiFixtureFactory,
-		fixtureSetBootJarsProductVariable("platform:foo"),
+		FixtureConfigureBootJars("platform:foo"),
 	).RunTestWithBp(t, `
 		java_import {
 			name: "foo",
@@ -157,7 +151,7 @@
 func TestHiddenAPISingletonWithPrebuiltUseSource(t *testing.T) {
 	result := android.GroupFixturePreparers(
 		hiddenApiFixtureFactory,
-		fixtureSetBootJarsProductVariable("platform:foo"),
+		FixtureConfigureBootJars("platform:foo"),
 	).RunTestWithBp(t, `
 		java_library {
 			name: "foo",
@@ -185,7 +179,7 @@
 func TestHiddenAPISingletonWithPrebuiltOverrideSource(t *testing.T) {
 	result := android.GroupFixturePreparers(
 		hiddenApiFixtureFactory,
-		fixtureSetBootJarsProductVariable("platform:foo"),
+		FixtureConfigureBootJars("platform:foo"),
 	).RunTestWithBp(t, `
 		java_library {
 			name: "foo",
@@ -295,7 +289,7 @@
 
 	result := android.GroupFixturePreparers(
 		hiddenApiFixtureFactory,
-		fixtureSetBootJarsProductVariable("platform:foo"),
+		FixtureConfigureBootJars("platform:foo"),
 		fixtureSetPrebuiltHiddenApiDirProductVariable(&prebuiltHiddenApiDir),
 	).RunTestWithBp(t, `
 		java_import {
diff --git a/java/java.go b/java/java.go
index fa7c96e..ee4f2eb 100644
--- a/java/java.go
+++ b/java/java.go
@@ -356,7 +356,7 @@
 	if javaVersion != "" {
 		return normalizeJavaVersion(ctx, javaVersion)
 	} else if ctx.Device() {
-		return defaultJavaLanguageVersion(ctx, sdkContext.SdkVersion())
+		return defaultJavaLanguageVersion(ctx, sdkContext.SdkVersion(ctx))
 	} else {
 		return JAVA_VERSION_9
 	}
@@ -463,6 +463,9 @@
 	// would the <x> library if <x> was configured as a boot jar.
 	j.initHiddenAPI(ctx, j.ConfigurationName())
 
+	j.sdkVersion = j.SdkVersion(ctx)
+	j.minSdkVersion = j.MinSdkVersion(ctx)
+
 	apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
 	if !apexInfo.IsForPlatform() {
 		j.hideApexVariantFromMake = true
@@ -1130,33 +1133,28 @@
 	exportAidlIncludeDirs android.Paths
 
 	hideApexVariantFromMake bool
+
+	sdkVersion    android.SdkSpec
+	minSdkVersion android.SdkSpec
 }
 
-func (j *Import) SdkVersion() android.SdkSpec {
-	return android.SdkSpecFrom(String(j.properties.Sdk_version))
-}
-
-func (j *Import) makeSdkVersion() string {
-	return j.SdkVersion().Raw
+func (j *Import) SdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
+	return android.SdkSpecFrom(ctx, String(j.properties.Sdk_version))
 }
 
 func (j *Import) SystemModules() string {
 	return "none"
 }
 
-func (j *Import) MinSdkVersion() android.SdkSpec {
+func (j *Import) MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
 	if j.properties.Min_sdk_version != nil {
-		return android.SdkSpecFrom(*j.properties.Min_sdk_version)
+		return android.SdkSpecFrom(ctx, *j.properties.Min_sdk_version)
 	}
-	return j.SdkVersion()
+	return j.SdkVersion(ctx)
 }
 
-func (j *Import) TargetSdkVersion() android.SdkSpec {
-	return j.SdkVersion()
-}
-
-func (j *Import) MinSdkVersionString() string {
-	return j.MinSdkVersion().ApiLevel.String()
+func (j *Import) TargetSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
+	return j.SdkVersion(ctx)
 }
 
 func (j *Import) Prebuilt() *android.Prebuilt {
@@ -1192,6 +1190,9 @@
 }
 
 func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	j.sdkVersion = j.SdkVersion(ctx)
+	j.minSdkVersion = j.MinSdkVersion(ctx)
+
 	// Initialize the hiddenapi structure.
 	j.initHiddenAPI(ctx, j.BaseModuleName())
 
@@ -1230,7 +1231,7 @@
 		} else if dep, ok := module.(SdkLibraryDependency); ok {
 			switch tag {
 			case libTag:
-				flags.classpath = append(flags.classpath, dep.SdkHeaderJars(ctx, j.SdkVersion())...)
+				flags.classpath = append(flags.classpath, dep.SdkHeaderJars(ctx, j.SdkVersion(ctx))...)
 			}
 		}
 
@@ -1291,7 +1292,7 @@
 			j.dexpreopter.uncompressedDex = *j.dexProperties.Uncompress_dex
 
 			var dexOutputFile android.OutputPath
-			dexOutputFile = j.dexer.compileDex(ctx, flags, j.MinSdkVersion(), outputFile, jarName)
+			dexOutputFile = j.dexer.compileDex(ctx, flags, j.MinSdkVersion(ctx), outputFile, jarName)
 			if ctx.Failed() {
 				return
 			}
@@ -1359,7 +1360,7 @@
 // Implements android.ApexModule
 func (j *Import) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
 	sdkVersion android.ApiLevel) error {
-	sdkSpec := j.MinSdkVersion()
+	sdkSpec := j.MinSdkVersion(ctx)
 	if !sdkSpec.Specified() {
 		return fmt.Errorf("min_sdk_version is not specified")
 	}
diff --git a/java/platform_bootclasspath.go b/java/platform_bootclasspath.go
index 86ab708..621119e 100644
--- a/java/platform_bootclasspath.go
+++ b/java/platform_bootclasspath.go
@@ -73,13 +73,13 @@
 	fragments []android.Module
 
 	// Path to the monolithic hiddenapi-flags.csv file.
-	hiddenAPIFlagsCSV android.Path
+	hiddenAPIFlagsCSV android.OutputPath
 
 	// Path to the monolithic hiddenapi-index.csv file.
-	hiddenAPIIndexCSV android.Path
+	hiddenAPIIndexCSV android.OutputPath
 
 	// Path to the monolithic hiddenapi-unsupported.csv file.
-	hiddenAPIMetadataCSV android.Path
+	hiddenAPIMetadataCSV android.OutputPath
 }
 
 // ApexVariantReference specifies a particular apex variant of a module.
@@ -208,7 +208,30 @@
 	// error, unless missing dependencies are allowed. The simplest way to handle that is to add a
 	// dependency that will not be satisfied and the default behavior will handle it.
 	if !addedDep {
-		ctx.AddFarVariationDependencies(variations, tag, name)
+		// Add dependency on the unprefixed (i.e. source or renamed prebuilt) module which we know does
+		// not exist. The resulting error message will contain useful information about the available
+		// variants.
+		reportMissingVariationDependency(ctx, variations, name)
+
+		// Add dependency on the missing prefixed prebuilt variant too if a module with that name exists
+		// so that information about its available variants will be reported too.
+		if ctx.OtherModuleExists(prebuiltName) {
+			reportMissingVariationDependency(ctx, variations, prebuiltName)
+		}
+	}
+}
+
+// reportMissingVariationDependency intentionally adds a dependency on a missing variation in order
+// to generate an appropriate error message with information about the available variations.
+func reportMissingVariationDependency(ctx android.BottomUpMutatorContext, variations []blueprint.Variation, name string) {
+	modules := ctx.AddFarVariationDependencies(variations, nil, name)
+	if len(modules) != 1 {
+		panic(fmt.Errorf("Internal Error: expected one module, found %d", len(modules)))
+		return
+	}
+	if modules[0] != nil {
+		panic(fmt.Errorf("Internal Error: expected module to be missing but was found: %q", modules[0]))
+		return
 	}
 }
 
@@ -261,26 +284,54 @@
 // generateHiddenAPIBuildActions generates all the hidden API related build rules.
 func (b *platformBootclasspathModule) generateHiddenAPIBuildActions(ctx android.ModuleContext, modules []android.Module) {
 
-	// Save the paths to the monolithic files for retrieval via OutputFiles()
-	// Make the paths relative to the out/soong/hiddenapi directory instead of to the out/soong/
-	// directory. This ensures that if they are used as java_resources they do not end up in a
-	// hiddenapi directory in the resulting APK.
-	relToHiddenapiDir := func(path android.OutputPath) android.Path {
-		return path
+	// Save the paths to the monolithic files for retrieval via OutputFiles().
+	b.hiddenAPIFlagsCSV = hiddenAPISingletonPaths(ctx).flags
+	b.hiddenAPIIndexCSV = hiddenAPISingletonPaths(ctx).index
+	b.hiddenAPIMetadataCSV = hiddenAPISingletonPaths(ctx).metadata
+
+	// Don't run any hiddenapi rules if UNSAFE_DISABLE_HIDDENAPI_FLAGS=true. This is a performance
+	// optimization that can be used to reduce the incremental build time but as its name suggests it
+	// can be unsafe to use, e.g. when the changes affect anything that goes on the bootclasspath.
+	if ctx.Config().IsEnvTrue("UNSAFE_DISABLE_HIDDENAPI_FLAGS") {
+		paths := android.OutputPaths{b.hiddenAPIFlagsCSV, b.hiddenAPIIndexCSV, b.hiddenAPIMetadataCSV}
+		for _, path := range paths {
+			ctx.Build(pctx, android.BuildParams{
+				Rule:   android.Touch,
+				Output: path,
+			})
+		}
+		return
 	}
-	b.hiddenAPIFlagsCSV = relToHiddenapiDir(hiddenAPISingletonPaths(ctx).flags)
-	b.hiddenAPIIndexCSV = relToHiddenapiDir(hiddenAPISingletonPaths(ctx).index)
-	b.hiddenAPIMetadataCSV = relToHiddenapiDir(hiddenAPISingletonPaths(ctx).metadata)
+
+	hiddenAPISupportingModules := []hiddenAPISupportingModule{}
+	for _, module := range modules {
+		if h, ok := module.(hiddenAPISupportingModule); ok {
+			if h.bootDexJar() == nil {
+				ctx.ModuleErrorf("module %s does not provide a bootDexJar file", module)
+			}
+			if h.flagsCSV() == nil {
+				ctx.ModuleErrorf("module %s does not provide a flagsCSV file", module)
+			}
+			if h.indexCSV() == nil {
+				ctx.ModuleErrorf("module %s does not provide an indexCSV file", module)
+			}
+			if h.metadataCSV() == nil {
+				ctx.ModuleErrorf("module %s does not provide a metadataCSV file", module)
+			}
+
+			if ctx.Failed() {
+				continue
+			}
+
+			hiddenAPISupportingModules = append(hiddenAPISupportingModules, h)
+		} else {
+			ctx.ModuleErrorf("module %s of type %s does not support hidden API processing", module, ctx.OtherModuleType(module))
+		}
+	}
 
 	moduleSpecificFlagsPaths := android.Paths{}
-	for _, module := range modules {
-		if h, ok := module.(hiddenAPIIntf); ok {
-			if csv := h.flagsCSV(); csv != nil {
-				moduleSpecificFlagsPaths = append(moduleSpecificFlagsPaths, csv)
-			}
-		} else {
-			ctx.ModuleErrorf("module %s of type %s does not implement hiddenAPIIntf", module, ctx.OtherModuleType(module))
-		}
+	for _, module := range hiddenAPISupportingModules {
+		moduleSpecificFlagsPaths = append(moduleSpecificFlagsPaths, module.flagsCSV())
 	}
 
 	augmentationInfo := b.properties.Hidden_api.hiddenAPIAugmentationInfo(ctx)
diff --git a/java/platform_bootclasspath_test.go b/java/platform_bootclasspath_test.go
index a0d0501..c740911 100644
--- a/java/platform_bootclasspath_test.go
+++ b/java/platform_bootclasspath_test.go
@@ -31,7 +31,7 @@
 func TestPlatformBootclasspath(t *testing.T) {
 	preparer := android.GroupFixturePreparers(
 		prepareForTestWithPlatformBootclasspath,
-		dexpreopt.FixtureSetBootJars("platform:foo", "platform:bar"),
+		FixtureConfigureBootJars("platform:foo", "platform:bar"),
 		android.FixtureWithRootAndroidBp(`
 			platform_bootclasspath {
 				name: "platform-bootclasspath",
@@ -135,7 +135,7 @@
 func TestPlatformBootclasspath_Dist(t *testing.T) {
 	result := android.GroupFixturePreparers(
 		prepareForTestWithPlatformBootclasspath,
-		dexpreopt.FixtureSetBootJars("platform:foo", "platform:bar"),
+		FixtureConfigureBootJars("platform:foo", "platform:bar"),
 		android.PrepareForTestWithAndroidMk,
 		android.FixtureWithRootAndroidBp(`
 			platform_bootclasspath {
diff --git a/java/prebuilt_apis.go b/java/prebuilt_apis.go
index 8a442b5..c33e6c2 100644
--- a/java/prebuilt_apis.go
+++ b/java/prebuilt_apis.go
@@ -253,14 +253,8 @@
 		files := getPrebuiltFilesInSubdir(mctx, nextApiDir, "api/*incompatibilities.txt")
 		for _, f := range files {
 			localPath := strings.TrimPrefix(f, mydir)
-			module, _, scope := parseApiFilePath(mctx, localPath)
-
-			// Figure out which module is referenced by this file. Special case for "android".
-			referencedModule := strings.TrimSuffix(module, "incompatibilities")
-			referencedModule = strings.TrimSuffix(referencedModule, "-")
-			if referencedModule == "" {
-				referencedModule = "android"
-			}
+			filename, _, scope := parseApiFilePath(mctx, localPath)
+			referencedModule := strings.TrimSuffix(filename, "-incompatibilities")
 
 			createApiModule(mctx, apiModuleName(referencedModule+"-incompatibilities", scope, "latest"), localPath)
 
diff --git a/java/rro.go b/java/rro.go
index 4ae0014..2e58c04 100644
--- a/java/rro.go
+++ b/java/rro.go
@@ -141,23 +141,23 @@
 	ctx.InstallFile(r.installDir, r.outputFile.Base(), r.outputFile)
 }
 
-func (r *RuntimeResourceOverlay) SdkVersion() android.SdkSpec {
-	return android.SdkSpecFrom(String(r.properties.Sdk_version))
+func (r *RuntimeResourceOverlay) SdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
+	return android.SdkSpecFrom(ctx, String(r.properties.Sdk_version))
 }
 
 func (r *RuntimeResourceOverlay) SystemModules() string {
 	return ""
 }
 
-func (r *RuntimeResourceOverlay) MinSdkVersion() android.SdkSpec {
+func (r *RuntimeResourceOverlay) MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
 	if r.properties.Min_sdk_version != nil {
-		return android.SdkSpecFrom(*r.properties.Min_sdk_version)
+		return android.SdkSpecFrom(ctx, *r.properties.Min_sdk_version)
 	}
-	return r.SdkVersion()
+	return r.SdkVersion(ctx)
 }
 
-func (r *RuntimeResourceOverlay) TargetSdkVersion() android.SdkSpec {
-	return r.SdkVersion()
+func (r *RuntimeResourceOverlay) TargetSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
+	return r.SdkVersion(ctx)
 }
 
 func (r *RuntimeResourceOverlay) Certificate() Certificate {
diff --git a/java/sdk.go b/java/sdk.go
index f324b76..d6e20a7 100644
--- a/java/sdk.go
+++ b/java/sdk.go
@@ -61,7 +61,7 @@
 }
 
 func decodeSdkDep(ctx android.EarlyModuleContext, sdkContext android.SdkContext) sdkDep {
-	sdkVersion := sdkContext.SdkVersion()
+	sdkVersion := sdkContext.SdkVersion(ctx)
 	if !sdkVersion.Valid() {
 		ctx.PropertyErrorf("sdk_version", "invalid version %q", sdkVersion.Raw)
 		return sdkDep{}
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 96135c3..e5ee397 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -1512,7 +1512,7 @@
 	// force override sdk_version to module_current so that the closest possible API
 	// surface could be found in selectHeaderJarsForSdkVersion
 	if module.defaultsToStubs() && !sdkVersion.Specified() {
-		sdkVersion = android.SdkSpecFrom("module_current")
+		sdkVersion = android.SdkSpecFrom(ctx, "module_current")
 	}
 
 	// Only provide access to the implementation library if it is actually built.
diff --git a/java/testing.go b/java/testing.go
index 6ebc747..aee0710 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -178,6 +178,43 @@
 	return fs
 }
 
+// FixtureConfigureBootJars configures the boot jars in both the dexpreopt.GlobalConfig and
+// Config.productVariables structs. As a side effect that enables dexpreopt.
+func FixtureConfigureBootJars(bootJars ...string) android.FixturePreparer {
+	artBootJars := []string{}
+	for _, j := range bootJars {
+		artApex := false
+		for _, artApexName := range artApexNames {
+			if strings.HasPrefix(j, artApexName+":") {
+				artApex = true
+				break
+			}
+		}
+		if artApex {
+			artBootJars = append(artBootJars, j)
+		}
+	}
+	return android.GroupFixturePreparers(
+		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+			variables.BootJars = android.CreateTestConfiguredJarList(bootJars)
+		}),
+		dexpreopt.FixtureSetBootJars(bootJars...),
+		dexpreopt.FixtureSetArtBootJars(artBootJars...),
+	)
+}
+
+// FixtureConfigureUpdatableBootJars configures the updatable boot jars in both the
+// dexpreopt.GlobalConfig and Config.productVariables structs. As a side effect that enables
+// dexpreopt.
+func FixtureConfigureUpdatableBootJars(bootJars ...string) android.FixturePreparer {
+	return android.GroupFixturePreparers(
+		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+			variables.UpdatableBootJars = android.CreateTestConfiguredJarList(bootJars)
+		}),
+		dexpreopt.FixtureSetUpdatableBootJars(bootJars...),
+	)
+}
+
 // registerRequiredBuildComponentsForTest registers the build components used by
 // PrepareForTestWithJavaDefaultModules.
 //
diff --git a/rust/Android.bp b/rust/Android.bp
index a29c474..a6c4e07 100644
--- a/rust/Android.bp
+++ b/rust/Android.bp
@@ -14,6 +14,7 @@
     ],
     srcs: [
         "androidmk.go",
+        "benchmark.go",
         "binary.go",
         "bindgen.go",
         "builder.go",
@@ -35,6 +36,7 @@
         "testing.go",
     ],
     testSrcs: [
+        "benchmark_test.go",
         "binary_test.go",
         "bindgen_test.go",
         "builder_test.go",
diff --git a/rust/androidmk.go b/rust/androidmk.go
index dea32a3..940710e 100644
--- a/rust/androidmk.go
+++ b/rust/androidmk.go
@@ -107,6 +107,20 @@
 	cc.AndroidMkWriteTestData(test.data, ret)
 }
 
+func (benchmark *benchmarkDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkEntries) {
+	benchmark.binaryDecorator.AndroidMk(ctx, ret)
+	ret.Class = "NATIVE_TESTS"
+	ret.ExtraEntries = append(ret.ExtraEntries,
+		func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+			entries.AddCompatibilityTestSuites(benchmark.Properties.Test_suites...)
+			if benchmark.testConfig != nil {
+				entries.SetString("LOCAL_FULL_TEST_CONFIG", benchmark.testConfig.String())
+			}
+			entries.SetBool("LOCAL_NATIVE_BENCHMARK", true)
+			entries.SetBoolIfTrue("LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG", !BoolDefault(benchmark.Properties.Auto_gen_config, true))
+		})
+}
+
 func (library *libraryDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkEntries) {
 	ctx.SubAndroidMk(ret, library.baseCompiler)
 
diff --git a/rust/benchmark.go b/rust/benchmark.go
new file mode 100644
index 0000000..b89f5cd
--- /dev/null
+++ b/rust/benchmark.go
@@ -0,0 +1,129 @@
+// Copyright 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package rust
+
+import (
+	"android/soong/android"
+	"android/soong/tradefed"
+)
+
+type BenchmarkProperties struct {
+	// Disables the creation of a test-specific directory when used with
+	// relative_install_path. Useful if several tests need to be in the same
+	// directory, but test_per_src doesn't work.
+	No_named_install_directory *bool
+
+	// the name of the test configuration (for example "AndroidBenchmark.xml") that should be
+	// installed with the module.
+	Test_config *string `android:"path,arch_variant"`
+
+	// the name of the test configuration template (for example "AndroidBenchmarkTemplate.xml") that
+	// should be installed with the module.
+	Test_config_template *string `android:"path,arch_variant"`
+
+	// list of compatibility suites (for example "cts", "vts") that the module should be
+	// installed into.
+	Test_suites []string `android:"arch_variant"`
+
+	// Flag to indicate whether or not to create test config automatically. If AndroidTest.xml
+	// doesn't exist next to the Android.bp, this attribute doesn't need to be set to true
+	// explicitly.
+	Auto_gen_config *bool
+}
+
+type benchmarkDecorator struct {
+	*binaryDecorator
+	Properties BenchmarkProperties
+	testConfig android.Path
+}
+
+func NewRustBenchmark(hod android.HostOrDeviceSupported) (*Module, *benchmarkDecorator) {
+	// Build both 32 and 64 targets for device benchmarks.
+	// Cannot build both for host benchmarks yet if the benchmark depends on
+	// something like proc-macro2 that cannot be built for both.
+	multilib := android.MultilibBoth
+	if hod != android.DeviceSupported && hod != android.HostAndDeviceSupported {
+		multilib = android.MultilibFirst
+	}
+	module := newModule(hod, multilib)
+
+	benchmark := &benchmarkDecorator{
+		binaryDecorator: &binaryDecorator{
+			baseCompiler: NewBaseCompiler("nativebench", "nativebench64", InstallInData),
+		},
+	}
+
+	module.compiler = benchmark
+	module.AddProperties(&benchmark.Properties)
+	return module, benchmark
+}
+
+func init() {
+	android.RegisterModuleType("rust_benchmark", RustBenchmarkFactory)
+	android.RegisterModuleType("rust_benchmark_host", RustBenchmarkHostFactory)
+}
+
+func RustBenchmarkFactory() android.Module {
+	module, _ := NewRustBenchmark(android.HostAndDeviceSupported)
+	return module.Init()
+}
+
+func RustBenchmarkHostFactory() android.Module {
+	module, _ := NewRustBenchmark(android.HostSupported)
+	return module.Init()
+}
+
+func (benchmark *benchmarkDecorator) autoDep(ctx android.BottomUpMutatorContext) autoDep {
+	return rlibAutoDep
+}
+
+func (benchmark *benchmarkDecorator) stdLinkage(ctx *depsContext) RustLinkage {
+	return RlibLinkage
+}
+
+func (benchmark *benchmarkDecorator) compilerFlags(ctx ModuleContext, flags Flags) Flags {
+	flags = benchmark.binaryDecorator.compilerFlags(ctx, flags)
+	return flags
+}
+
+func (benchmark *benchmarkDecorator) compilerDeps(ctx DepsContext, deps Deps) Deps {
+	deps = benchmark.binaryDecorator.compilerDeps(ctx, deps)
+
+	deps.Rustlibs = append(deps.Rustlibs, "libcriterion")
+
+	return deps
+}
+
+func (benchmark *benchmarkDecorator) compilerProps() []interface{} {
+	return append(benchmark.binaryDecorator.compilerProps(), &benchmark.Properties)
+}
+
+func (benchmark *benchmarkDecorator) install(ctx ModuleContext) {
+	benchmark.testConfig = tradefed.AutoGenRustBenchmarkConfig(ctx,
+		benchmark.Properties.Test_config,
+		benchmark.Properties.Test_config_template,
+		benchmark.Properties.Test_suites,
+		nil,
+		benchmark.Properties.Auto_gen_config)
+
+	// default relative install path is module name
+	if !Bool(benchmark.Properties.No_named_install_directory) {
+		benchmark.baseCompiler.relative = ctx.ModuleName()
+	} else if String(benchmark.baseCompiler.Properties.Relative_install_path) == "" {
+		ctx.PropertyErrorf("no_named_install_directory", "Module install directory may only be disabled if relative_install_path is set")
+	}
+
+	benchmark.binaryDecorator.install(ctx)
+}
diff --git a/rust/benchmark_test.go b/rust/benchmark_test.go
new file mode 100644
index 0000000..734dda7
--- /dev/null
+++ b/rust/benchmark_test.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 (
+	"strings"
+	"testing"
+
+	"android/soong/android"
+)
+
+func TestRustBenchmark(t *testing.T) {
+	ctx := testRust(t, `
+		rust_benchmark_host {
+			name: "my_bench",
+			srcs: ["foo.rs"],
+		}`)
+
+	testingModule := ctx.ModuleForTests("my_bench", "linux_glibc_x86_64")
+	expectedOut := "my_bench/linux_glibc_x86_64/my_bench"
+	outPath := testingModule.Output("my_bench").Output.String()
+	if !strings.Contains(outPath, expectedOut) {
+		t.Errorf("wrong output path: %v;  expected: %v", outPath, expectedOut)
+	}
+}
+
+func TestRustBenchmarkLinkage(t *testing.T) {
+	ctx := testRust(t, `
+		rust_benchmark {
+			name: "my_bench",
+			srcs: ["foo.rs"],
+		}`)
+
+	testingModule := ctx.ModuleForTests("my_bench", "android_arm64_armv8-a").Module().(*Module)
+
+	if !android.InList("libcriterion.rlib-std", testingModule.Properties.AndroidMkRlibs) {
+		t.Errorf("rlib-std variant for libcriterion not detected as a rustlib-defined rlib dependency for device rust_benchmark module")
+	}
+	if !android.InList("libstd", testingModule.Properties.AndroidMkRlibs) {
+		t.Errorf("Device rust_benchmark module 'my_bench' does not link libstd as an rlib")
+	}
+}
diff --git a/rust/rust.go b/rust/rust.go
index 34e197a..ca85d74 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -434,6 +434,7 @@
 	module.AddProperties(
 		&BaseProperties{},
 		&cc.VendorProperties{},
+		&BenchmarkProperties{},
 		&BindgenProperties{},
 		&BaseCompilerProperties{},
 		&BinaryCompilerProperties{},
diff --git a/rust/testing.go b/rust/testing.go
index 75adcfc..1e01cc0 100644
--- a/rust/testing.go
+++ b/rust/testing.go
@@ -192,11 +192,19 @@
 			srcs:["foo.rs"],
 			host_supported: true,
 		}
+		rust_library {
+			name: "libcriterion",
+			crate_name: "criterion",
+			srcs:["foo.rs"],
+			host_supported: true,
+		}
 `
 	return bp
 }
 
 func registerRequiredBuildComponentsForTest(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("rust_benchmark", RustBenchmarkFactory)
+	ctx.RegisterModuleType("rust_benchmark_host", RustBenchmarkHostFactory)
 	ctx.RegisterModuleType("rust_binary", RustBinaryFactory)
 	ctx.RegisterModuleType("rust_binary_host", RustBinaryHostFactory)
 	ctx.RegisterModuleType("rust_bindgen", RustBindgenFactory)
diff --git a/tests/lib.sh b/tests/lib.sh
index 3c97e14..b61ca91 100644
--- a/tests/lib.sh
+++ b/tests/lib.sh
@@ -54,7 +54,7 @@
     mkdir -p "$MOCK_TOP"
   else
     MOCK_TOP=$(mktemp -t -d st.XXXXX)
-    trap 'echo cd / && echo rm -fr "$MOCK_TOP"' EXIT
+    trap 'cd / && rm -fr "$MOCK_TOP"' EXIT
   fi
 
   echo "Test case: ${FUNCNAME[1]}, mock top path: $MOCK_TOP"
diff --git a/tradefed/autogen.go b/tradefed/autogen.go
index 27d71e8..3d96c84 100644
--- a/tradefed/autogen.go
+++ b/tradefed/autogen.go
@@ -245,6 +245,25 @@
 	return path
 }
 
+func AutoGenRustBenchmarkConfig(ctx android.ModuleContext, testConfigProp *string,
+	testConfigTemplateProp *string, testSuites []string, config []Config, autoGenConfig *bool) android.Path {
+	path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites, autoGenConfig, testConfigTemplateProp)
+	if autogenPath != nil {
+		templatePath := getTestConfigTemplate(ctx, testConfigTemplateProp)
+		if templatePath.Valid() {
+			autogenTemplate(ctx, autogenPath, templatePath.String(), config, "")
+		} else {
+			if ctx.Device() {
+				autogenTemplate(ctx, autogenPath, "${RustDeviceBenchmarkConfigTemplate}", config, "")
+			} else {
+				autogenTemplate(ctx, autogenPath, "${RustHostBenchmarkConfigTemplate}", config, "")
+			}
+		}
+		return autogenPath
+	}
+	return path
+}
+
 func AutoGenRobolectricTestConfig(ctx android.ModuleContext, testConfigProp *string, testConfigTemplateProp *string,
 	testSuites []string, autoGenConfig *bool) android.Path {
 	path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites, autoGenConfig, testConfigTemplateProp)
diff --git a/tradefed/config.go b/tradefed/config.go
index f3566a8..999424c 100644
--- a/tradefed/config.go
+++ b/tradefed/config.go
@@ -34,6 +34,8 @@
 	pctx.SourcePathVariable("PythonBinaryHostTestConfigTemplate", "build/make/core/python_binary_host_test_config_template.xml")
 	pctx.SourcePathVariable("RustDeviceTestConfigTemplate", "build/make/core/rust_device_test_config_template.xml")
 	pctx.SourcePathVariable("RustHostTestConfigTemplate", "build/make/core/rust_host_test_config_template.xml")
+	pctx.SourcePathVariable("RustDeviceBenchmarkConfigTemplate", "build/make/core/rust_device_benchmark_config_template.xml")
+	pctx.SourcePathVariable("RustHostBenchmarkConfigTemplate", "build/make/core/rust_host_benchmark_config_template.xml")
 	pctx.SourcePathVariable("RobolectricTestConfigTemplate", "build/make/core/robolectric_test_config_template.xml")
 	pctx.SourcePathVariable("ShellTestConfigTemplate", "build/make/core/shell_test_config_template.xml")
 
diff --git a/tradefed/makevars.go b/tradefed/makevars.go
index f9682e4..9b5a20f 100644
--- a/tradefed/makevars.go
+++ b/tradefed/makevars.go
@@ -33,6 +33,8 @@
 	ctx.Strict("PYTHON_BINARY_HOST_TEST_CONFIG_TEMPLATE", "${PythonBinaryHostTestConfigTemplate}")
 	ctx.Strict("RUST_DEVICE_TEST_CONFIG_TEMPLATE", "${RustDeviceTestConfigTemplate}")
 	ctx.Strict("RUST_HOST_TEST_CONFIG_TEMPLATE", "${RustHostTestConfigTemplate}")
+	ctx.Strict("RUST_DEVICE_BENCHMARK_CONFIG_TEMPLATE", "${RustDeviceBenchmarkConfigTemplate}")
+	ctx.Strict("RUST_HOST_BENCHMARK_CONFIG_TEMPLATE", "${RustHostBenchmarkConfigTemplate}")
 	ctx.Strict("SHELL_TEST_CONFIG_TEMPLATE", "${ShellTestConfigTemplate}")
 
 	ctx.Strict("EMPTY_TEST_CONFIG", "${EmptyTestConfig}")
diff --git a/zip/zip.go b/zip/zip.go
index a6490d4..84e974b 100644
--- a/zip/zip.go
+++ b/zip/zip.go
@@ -292,11 +292,11 @@
 				continue
 			}
 
-			globbed, _, err := z.fs.Glob(s, nil, followSymlinks)
+			result, err := z.fs.Glob(s, nil, followSymlinks)
 			if err != nil {
 				return err
 			}
-			if len(globbed) == 0 {
+			if len(result.Matches) == 0 {
 				err := &os.PathError{
 					Op:   "lstat",
 					Path: s,
@@ -308,7 +308,7 @@
 					return err
 				}
 			}
-			srcs = append(srcs, globbed...)
+			srcs = append(srcs, result.Matches...)
 		}
 		if fa.GlobDir != "" {
 			if exists, isDir, err := z.fs.Exists(fa.GlobDir); err != nil {
@@ -336,11 +336,11 @@
 					return err
 				}
 			}
-			globbed, _, err := z.fs.Glob(filepath.Join(fa.GlobDir, "**/*"), nil, followSymlinks)
+			result, err := z.fs.Glob(filepath.Join(fa.GlobDir, "**/*"), nil, followSymlinks)
 			if err != nil {
 				return err
 			}
-			srcs = append(srcs, globbed...)
+			srcs = append(srcs, result.Matches...)
 		}
 		for _, src := range srcs {
 			err := fillPathPairs(fa, src, &pathMappings, args.NonDeflatedFiles, noCompression)