Merge "Add build-command arg to soong_ui."
diff --git a/README.md b/README.md
index 18cf7b2..7f18463 100644
--- a/README.md
+++ b/README.md
@@ -609,15 +609,15 @@
   Content Root, then add the `build/blueprint` directory.
 * Optional: also add the `external/golang-protobuf` directory. In practice,
   IntelliJ seems to work well enough without this, too.
+
 ### Running Soong in a debugger
 
-To make `soong_build` wait for a debugger connection, install `dlv` and then
-start the build with `SOONG_DELVE=<listen addr>` in the environment.
-For example:
-```bash
-SOONG_DELVE=5006 m nothing
-```
+Both the Android build driver (`soong_ui`) and Soong proper (`soong_build`) are
+Go applications and can be debugged with the help of the standard Go debugger
+called Delve. A client (e.g., IntelliJ IDEA) communicates with Delve via IP port
+that Delve listens to (the port number is passed to it on invocation).
 
+#### Debugging Android Build Driver ####
 To make `soong_ui` wait for a debugger connection, use the `SOONG_UI_DELVE`
 variable:
 
@@ -625,11 +625,28 @@
 SOONG_UI_DELVE=5006 m nothing
 ```
 
+#### Debugging Soong Proper ####
 
-setting or unsetting `SOONG_DELVE` causes a recompilation of `soong_build`. This
+To make `soong_build` wait for a debugger connection, install `dlv` and then
+start the build with `SOONG_DELVE=<listen addr>` in the environment.
+For example:
+```bash
+SOONG_DELVE=5006 m nothing
+```
+Android build driver invokes `soong_build` multiple times, and by default each
+invocation is run in the debugger. Setting `SOONG_DELVE_STEPS` controls which
+invocations are run in the debugger, e.g., running
+```bash
+SOONG_DELVE=2345 SOONG_DELVE_STEPS='build,modulegraph' m
+```
+results in only `build` (main build step) and `modulegraph` being run in the debugger.
+The allowed step names are `api_bp2build`, `bp2build_files`, `bp2build_workspace`,
+`build`, `modulegraph`, `queryview`, `soong_docs`.
+
+Note setting or unsetting `SOONG_DELVE` causes a recompilation of `soong_build`. This
 is because in order to debug the binary, it needs to be built with debug
 symbols.
-
+#### Delve Troubleshooting ####
 To test the debugger connection, run this command:
 
 ```
@@ -648,15 +665,23 @@
 sudo sysctl -w kernel.yama.ptrace_scope=0
 ```
 
+#### IntelliJ Setup ####
 To connect to the process using IntelliJ:
 
 * Run -> Edit Configurations...
 * Choose "Go Remote" on the left
 * Click on the "+" buttion on the top-left
-* Give it a nice name and set "Host" to localhost and "Port" to the port in the
-  environment variable
+* Give it a nice _name_ and set "Host" to `localhost` and "Port" to the port in the
+  environment variable (`SOONG_UI_DELVE` for `soong_ui`, `SOONG_DELVE` for
+  `soong_build`)
+* Set the breakpoints where you want application to stop
+* Run the build from the command line
+* In IntelliJ, click Run -> Debug _name_
+* Observe _Connecting..._ message in the debugger pane. It changes to
+  _Connected_ once the communication with the debugger has been established; the
+  terminal window where the build started will display
+  `API server listening at ...` message
 
-Debugging works far worse than debugging Java, but is sometimes useful.
 
 Sometimes the `dlv` process hangs on connection. A symptom of this is `dlv`
 spinning a core or two. In that case, `kill -9` `dlv` and try again.
diff --git a/android/allowlists/allowlists.go b/android/allowlists/allowlists.go
index 5658503..e7bd920 100644
--- a/android/allowlists/allowlists.go
+++ b/android/allowlists/allowlists.go
@@ -387,8 +387,11 @@
 		"prebuilts/sdk":/* recursive = */ false,
 		"prebuilts/sdk/tools":/* recursive = */ false,
 		"prebuilts/r8":/* recursive = */ false,
+		"prebuilts/runtime":/* recursive = */ false,
 
-		"tools/asuite/atest/":/* recursive = */ true,
+		// not recursive due to conflicting workspace paths in tools/atest/bazel/rules
+		"tools/asuite/atest":/* recursive = */ false,
+		"tools/asuite/atest/bazel/reporter":/* recursive = */ true,
 	}
 
 	Bp2buildModuleAlwaysConvertList = []string{
diff --git a/android/androidmk.go b/android/androidmk.go
index 18e3e7a..846d506 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -504,6 +504,7 @@
 	Config() Config
 	ModuleProvider(module blueprint.Module, provider blueprint.ProviderKey) interface{}
 	ModuleHasProvider(module blueprint.Module, provider blueprint.ProviderKey) bool
+	ModuleType(module blueprint.Module) string
 }
 
 func (a *AndroidMkEntries) fillInEntries(ctx fillInEntriesContext, mod blueprint.Module) {
@@ -527,7 +528,7 @@
 		fmt.Fprintf(&a.header, distString)
 	}
 
-	fmt.Fprintln(&a.header, "\ninclude $(CLEAR_VARS)")
+	fmt.Fprintln(&a.header, "\ninclude $(CLEAR_VARS)  # "+ctx.ModuleType(mod))
 
 	// Collect make variable assignment entries.
 	a.SetString("LOCAL_PATH", ctx.ModuleDir(mod))
diff --git a/android/bazel_handler.go b/android/bazel_handler.go
index acb81a4..cf74b9c 100644
--- a/android/bazel_handler.go
+++ b/android/bazel_handler.go
@@ -18,7 +18,6 @@
 	"bytes"
 	"errors"
 	"fmt"
-	"io/ioutil"
 	"os"
 	"os/exec"
 	"path"
@@ -260,11 +259,11 @@
 	return result, nil
 }
 
-func (m MockBazelContext) InvokeBazel(_ Config, ctx *Context) error {
+func (m MockBazelContext) InvokeBazel(_ Config, _ *Context) error {
 	panic("unimplemented")
 }
 
-func (m MockBazelContext) BazelAllowlisted(moduleName string) bool {
+func (m MockBazelContext) BazelAllowlisted(_ string) bool {
 	return true
 }
 
@@ -356,7 +355,7 @@
 	panic("implement me")
 }
 
-func (n noopBazelContext) InvokeBazel(_ Config, ctx *Context) error {
+func (n noopBazelContext) InvokeBazel(_ Config, _ *Context) error {
 	panic("unimplemented")
 }
 
@@ -364,7 +363,7 @@
 	return ""
 }
 
-func (n noopBazelContext) BazelAllowlisted(moduleName string) bool {
+func (n noopBazelContext) BazelAllowlisted(_ string) bool {
 	return false
 }
 
@@ -403,7 +402,7 @@
 		// Don't use partially-converted cc_library targets in mixed builds,
 		// since mixed builds would generally rely on both static and shared
 		// variants of a cc_library.
-		for staticOnlyModule, _ := range GetBp2BuildAllowList().ccLibraryStaticOnly {
+		for staticOnlyModule := range GetBp2BuildAllowList().ccLibraryStaticOnly {
 			disabledModules[staticOnlyModule] = true
 		}
 		for _, disabledDevModule := range allowlists.MixedBuildsDisabledList {
@@ -509,7 +508,7 @@
 	extraFlags []string
 }
 
-func (r *mockBazelRunner) createBazelCommand(paths *bazelPaths, runName bazel.RunName,
+func (r *mockBazelRunner) createBazelCommand(_ *bazelPaths, _ bazel.RunName,
 	command bazelCommand, extraFlags ...string) *exec.Cmd {
 	r.commands = append(r.commands, command)
 	r.extraFlags = append(r.extraFlags, strings.Join(extraFlags, " "))
@@ -534,13 +533,13 @@
 // Returns (stdout, stderr, error). The first and second return values are strings
 // containing the stdout and stderr of the run command, and an error is returned if
 // the invocation returned an error code.
-
 func (r *builtinBazelRunner) issueBazelCommand(bazelCmd *exec.Cmd) (string, string, error) {
 	stderr := &bytes.Buffer{}
 	bazelCmd.Stderr = stderr
 	if output, err := bazelCmd.Output(); err != nil {
 		return "", string(stderr.Bytes()),
-			fmt.Errorf("bazel command failed. command: [%s], env: [%s], error [%s]", bazelCmd, bazelCmd.Env, stderr)
+			fmt.Errorf("bazel command failed: %s\n---command---\n%s\n---env---\n%s\n---stderr---\n%s---",
+				err, bazelCmd, strings.Join(bazelCmd.Env, "\n"), stderr)
 	} else {
 		return string(output), string(stderr.Bytes()), nil
 	}
@@ -916,17 +915,17 @@
 			return err
 		}
 	}
-	if err := ioutil.WriteFile(filepath.Join(soongInjectionPath, "WORKSPACE.bazel"), []byte{}, 0666); err != nil {
+	if err := os.WriteFile(filepath.Join(soongInjectionPath, "WORKSPACE.bazel"), []byte{}, 0666); err != nil {
 		return err
 	}
-	if err := ioutil.WriteFile(filepath.Join(mixedBuildsPath, "main.bzl"), context.mainBzlFileContents(), 0666); err != nil {
+	if err := os.WriteFile(filepath.Join(mixedBuildsPath, "main.bzl"), context.mainBzlFileContents(), 0666); err != nil {
 		return err
 	}
-	if err := ioutil.WriteFile(filepath.Join(mixedBuildsPath, "BUILD.bazel"), context.mainBuildFileContents(), 0666); err != nil {
+	if err := os.WriteFile(filepath.Join(mixedBuildsPath, "BUILD.bazel"), context.mainBuildFileContents(), 0666); err != nil {
 		return err
 	}
 	cqueryFileRelpath := filepath.Join(context.paths.injectedFilesDir(), "buildroot.cquery")
-	if err := ioutil.WriteFile(absolutePath(cqueryFileRelpath), context.cqueryStarlarkFileContents(), 0666); err != nil {
+	if err := os.WriteFile(absolutePath(cqueryFileRelpath), context.cqueryStarlarkFileContents(), 0666); err != nil {
 		return err
 	}
 
@@ -937,7 +936,7 @@
 		return cqueryErr
 	}
 	cqueryCommandPrint := fmt.Sprintf("cquery command line:\n  %s \n\n\n", printableCqueryCommand(cqueryCommandWithFlag))
-	if err := ioutil.WriteFile(filepath.Join(soongInjectionPath, "cquery.out"), []byte(cqueryCommandPrint+cqueryOutput), 0666); err != nil {
+	if err := os.WriteFile(filepath.Join(soongInjectionPath, "cquery.out"), []byte(cqueryCommandPrint+cqueryOutput), 0666); err != nil {
 		return err
 	}
 	cqueryResults := map[string]string{}
@@ -972,7 +971,7 @@
 		extraFlags = append(extraFlags, "--collect_code_coverage")
 		paths := make([]string, 0, 2)
 		if p := config.productVariables.NativeCoveragePaths; len(p) > 0 {
-			for i, _ := range p {
+			for i := range p {
 				// TODO(b/259404593) convert path wildcard to regex values
 				if p[i] == "*" {
 					p[i] = ".*"
@@ -1039,7 +1038,7 @@
 		filepath.Dir(ctx.Config().moduleListFile), "bazel.list"))
 	ctx.AddNinjaFileDeps(bazelBuildList)
 
-	data, err := ioutil.ReadFile(bazelBuildList)
+	data, err := os.ReadFile(bazelBuildList)
 	if err != nil {
 		ctx.Errorf(err.Error())
 	}
diff --git a/android/fixture.go b/android/fixture.go
index 3f01f5a..c2b16f6 100644
--- a/android/fixture.go
+++ b/android/fixture.go
@@ -213,6 +213,46 @@
 	})
 }
 
+// FixtureTestRunner determines the type of test to run.
+//
+// If no custom FixtureTestRunner is provided (using the FixtureSetTestRunner) then the default test
+// runner will run a standard Soong test that corresponds to what happens when Soong is run on the
+// command line.
+type FixtureTestRunner interface {
+	// FinalPreparer is a function that is run immediately before parsing the blueprint files. It is
+	// intended to perform the initialization needed by PostParseProcessor.
+	//
+	// It returns a CustomTestResult that is passed into PostParseProcessor and returned from
+	// FixturePreparer.RunTestWithCustomResult. If it needs to return some custom data then it must
+	// provide its own implementation of CustomTestResult and return an instance of that. Otherwise,
+	// it can just return the supplied *TestResult.
+	FinalPreparer(result *TestResult) CustomTestResult
+
+	// PostParseProcessor is called after successfully parsing the blueprint files and can do further
+	// work on the result of parsing the files.
+	//
+	// Successfully parsing simply means that no errors were encountered when parsing the blueprint
+	// files.
+	//
+	// This must collate any information useful for testing, e.g. errs, ninja deps and custom data in
+	// the supplied result.
+	PostParseProcessor(result CustomTestResult)
+}
+
+// FixtureSetTestRunner sets the FixtureTestRunner in the fixture.
+//
+// It is an error if more than one of these is applied to a single fixture. If none of these are
+// applied then the fixture will use the defaultTestRunner which will run the test as if it was
+// being run in `m <target>`.
+func FixtureSetTestRunner(testRunner FixtureTestRunner) FixturePreparer {
+	return newSimpleFixturePreparer(func(fixture *fixture) {
+		if fixture.testRunner != nil {
+			panic("fixture test runner has already been set")
+		}
+		fixture.testRunner = testRunner
+	})
+}
+
 // Modify the config
 func FixtureModifyConfig(mutator func(config Config)) FixturePreparer {
 	return newSimpleFixturePreparer(func(f *fixture) {
@@ -391,6 +431,21 @@
 	// Shorthand for Fixture(t).RunTest()
 	RunTest(t *testing.T) *TestResult
 
+	// RunTestWithCustomResult runs the test just as RunTest(t) does but instead of returning a
+	// *TestResult it returns the CustomTestResult that was returned by the custom
+	// FixtureTestRunner.PostParseProcessor method that ran the test, or the *TestResult if that
+	// method returned nil.
+	//
+	// This method must be used when needing to access custom data collected by the
+	// FixtureTestRunner.PostParseProcessor method.
+	//
+	// e.g. something like this
+	//
+	//   preparers := ...FixtureSetTestRunner(&myTestRunner)...
+	//   customResult := preparers.RunTestWithCustomResult(t).(*myCustomTestResult)
+	//   doSomething(customResult.data)
+	RunTestWithCustomResult(t *testing.T) CustomTestResult
+
 	// Run the test with the supplied Android.bp file.
 	//
 	// preparer.RunTestWithBp(t, bp) is shorthand for
@@ -619,7 +674,7 @@
 	MockFS() MockFS
 
 	// Run the test, checking any errors reported and returning a TestResult instance.
-	RunTest() *TestResult
+	RunTest() CustomTestResult
 }
 
 // Struct to allow TestResult to embed a *TestContext and allow call forwarding to its methods.
@@ -642,6 +697,39 @@
 	NinjaDeps []string
 }
 
+func (r *TestResult) testResult() *TestResult { return r }
+
+// CustomTestResult is the interface that FixtureTestRunner implementations who wish to return
+// custom data must implement. It must embed *TestResult and initialize that to the value passed
+// into the method. It is returned from the FixtureTestRunner.FinalPreparer, passed into the
+// FixtureTestRunner.PostParseProcessor and returned from FixturePreparer.RunTestWithCustomResult.
+//
+// e.g. something like this:
+//
+//		type myCustomTestResult struct {
+//		    *android.TestResult
+//		    data []string
+//		}
+//
+//		func (r *myTestRunner) FinalPreparer(result *TestResult) CustomTestResult {
+//	     ... do some final test preparation ...
+//	     return &myCustomTestResult{TestResult: result)
+//	 }
+//
+//		func (r *myTestRunner) PostParseProcessor(result CustomTestResult) {
+//		    ...
+//		    myData := []string {....}
+//		    ...
+//		    customResult := result.(*myCustomTestResult)
+//	     customResult.data = myData
+//		}
+type CustomTestResult interface {
+	// testResult returns the embedded *TestResult.
+	testResult() *TestResult
+}
+
+var _ CustomTestResult = (*TestResult)(nil)
+
 type TestPathContext struct {
 	*TestResult
 }
@@ -696,6 +784,11 @@
 
 func (b *baseFixturePreparer) RunTest(t *testing.T) *TestResult {
 	t.Helper()
+	return b.RunTestWithCustomResult(t).testResult()
+}
+
+func (b *baseFixturePreparer) RunTestWithCustomResult(t *testing.T) CustomTestResult {
+	t.Helper()
 	fixture := b.self.Fixture(t)
 	return fixture.RunTest()
 }
@@ -724,13 +817,16 @@
 		ctx.SetModuleListFile(ctx.config.mockBpList)
 	}
 
-	return fixture.RunTest()
+	return fixture.RunTest().testResult()
 }
 
 type fixture struct {
 	// The preparers used to create this fixture.
 	preparers []*simpleFixturePreparer
 
+	// The test runner used in this fixture, defaults to defaultTestRunner if not set.
+	testRunner FixtureTestRunner
+
 	// The gotest state of the go test within which this was created.
 	t *testing.T
 
@@ -762,7 +858,7 @@
 	return f.mockFS
 }
 
-func (f *fixture) RunTest() *TestResult {
+func (f *fixture) RunTest() CustomTestResult {
 	f.t.Helper()
 
 	// If in debug mode output the state of the fixture before running the test.
@@ -800,30 +896,59 @@
 	// Set the NameResolver in the TestContext.
 	ctx.NameResolver = resolver
 
-	ctx.Register()
-	var ninjaDeps []string
-	extraNinjaDeps, errs := ctx.ParseBlueprintsFiles("ignored")
-	if len(errs) == 0 {
-		ninjaDeps = append(ninjaDeps, extraNinjaDeps...)
-		extraNinjaDeps, errs = ctx.PrepareBuildActions(f.config)
-		if len(errs) == 0 {
-			ninjaDeps = append(ninjaDeps, extraNinjaDeps...)
-		}
+	// If test runner has not been set then use the default runner.
+	if f.testRunner == nil {
+		f.testRunner = defaultTestRunner
 	}
 
+	// Create the result to collate result information.
 	result := &TestResult{
 		testContext: testContext{ctx},
 		fixture:     f,
 		Config:      f.config,
-		Errs:        errs,
-		NinjaDeps:   ninjaDeps,
+	}
+
+	// Do any last minute preparation before parsing the blueprint files.
+	customResult := f.testRunner.FinalPreparer(result)
+
+	// Parse the blueprint files adding the information to the result.
+	extraNinjaDeps, errs := ctx.ParseBlueprintsFiles("ignored")
+	result.NinjaDeps = append(result.NinjaDeps, extraNinjaDeps...)
+	result.Errs = append(result.Errs, errs...)
+
+	if len(result.Errs) == 0 {
+		// If parsing the blueprint files was successful then perform any additional processing.
+		f.testRunner.PostParseProcessor(customResult)
 	}
 
 	f.errorHandler.CheckErrors(f.t, result)
 
+	return customResult
+}
+
+// standardTestRunner is the implementation of the default test runner
+type standardTestRunner struct{}
+
+func (s *standardTestRunner) FinalPreparer(result *TestResult) CustomTestResult {
+	// Register the hard coded mutators and singletons used by the standard Soong build as well as
+	// any additional instances that have been registered with this fixture.
+	result.TestContext.Register()
 	return result
 }
 
+func (s *standardTestRunner) PostParseProcessor(customResult CustomTestResult) {
+	result := customResult.(*TestResult)
+	ctx := result.TestContext
+	cfg := result.Config
+	// Prepare the build actions, i.e. run all the mutators, singletons and then invoke the
+	// GenerateAndroidBuildActions methods on all the modules.
+	extraNinjaDeps, errs := ctx.PrepareBuildActions(cfg)
+	result.NinjaDeps = append(result.NinjaDeps, extraNinjaDeps...)
+	result.CollateErrs(errs)
+}
+
+var defaultTestRunner FixtureTestRunner = &standardTestRunner{}
+
 func (f *fixture) outputDebugState() {
 	fmt.Printf("Begin Fixture State for %s\n", f.t.Name())
 	if len(f.config.env) == 0 {
@@ -909,3 +1034,10 @@
 func (r *TestResult) Module(name string, variant string) Module {
 	return r.ModuleForTests(name, variant).Module()
 }
+
+// CollateErrs adds additional errors to the result and returns true if there is more than one
+// error in the result.
+func (r *TestResult) CollateErrs(errs []error) bool {
+	r.Errs = append(r.Errs, errs...)
+	return len(r.Errs) > 0
+}
diff --git a/android/mutator.go b/android/mutator.go
index 83d4e66..d92b87c 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -705,6 +705,28 @@
 	t.createBazelTargetModule(bazelProps, commonAttrs, attrs, enabledProperty)
 }
 
+// ApexAvailableTags converts the apex_available property value of an ApexModule
+// module and returns it as a list of keyed tags.
+func ApexAvailableTags(mod Module) bazel.StringListAttribute {
+	attr := bazel.StringListAttribute{}
+	tags := []string{}
+	// Transform specific attributes into tags.
+	if am, ok := mod.(ApexModule); ok {
+		// TODO(b/218841706): hidl_interface has the apex_available prop, but it's
+		// defined directly as a prop and not via ApexModule, so this doesn't
+		// pick those props up.
+		// TODO(b/260694842): This does not pick up aidl_interface.backend.ndk.apex_available.
+		for _, a := range am.apexModuleBase().ApexAvailable() {
+			tags = append(tags, "apex_available="+a)
+		}
+	}
+	if len(tags) > 0 {
+		// This avoids creating a tags attr with an empty list if there are no tags.
+		attr.Value = tags
+	}
+	return attr
+}
+
 func (t *topDownMutatorContext) createBazelTargetModule(
 	bazelProps bazel.BazelTargetModuleProperties,
 	commonAttrs CommonAttributes,
diff --git a/android/testing.go b/android/testing.go
index 8fcf440..29af71f 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -203,6 +203,10 @@
 	ctx.PreArchMutators(f)
 }
 
+func (ctx *TestContext) ModuleProvider(m blueprint.Module, p blueprint.ProviderKey) interface{} {
+	return ctx.Context.ModuleProvider(m, p)
+}
+
 func (ctx *TestContext) PreDepsMutators(f RegisterMutatorFunc) {
 	ctx.preDeps = append(ctx.preDeps, f)
 }
diff --git a/apex/androidmk.go b/apex/androidmk.go
index 3373211..0fc971b 100644
--- a/apex/androidmk.go
+++ b/apex/androidmk.go
@@ -134,7 +134,7 @@
 			continue
 		}
 
-		fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)")
+		fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)  # apex.apexBundle.files")
 		if fi.moduleDir != "" {
 			fmt.Fprintln(w, "LOCAL_PATH :=", fi.moduleDir)
 		} else {
@@ -348,7 +348,7 @@
 
 			if apexType == flattenedApex {
 				// Only image APEXes can be flattened.
-				fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)")
+				fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)  # apex.apexBundle.flat")
 				fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir)
 				fmt.Fprintln(w, "LOCAL_MODULE :=", name+a.suffix)
 				data.Entries.WriteLicenseVariables(w)
@@ -356,7 +356,7 @@
 				fmt.Fprintln(w, "include $(BUILD_PHONY_PACKAGE)")
 
 			} else {
-				fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)")
+				fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)  # apex.apexBundle")
 				fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir)
 				fmt.Fprintln(w, "LOCAL_MODULE :=", name+a.suffix)
 				data.Entries.WriteLicenseVariables(w)
diff --git a/apex/apex.go b/apex/apex.go
index b1b4e47..8e1783e 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -2676,6 +2676,10 @@
 	}
 	attrs, props := convertWithBp2build(a, ctx)
 
+	// We just want the name, not module reference.
+	baseApexName := strings.TrimPrefix(baseApexModuleName, ":")
+	attrs.Base_apex_name = &baseApexName
+
 	for _, p := range o.GetProperties() {
 		overridableProperties, ok := p.(*overridableProperties)
 		if !ok {
@@ -3397,6 +3401,7 @@
 	Package_name          *string
 	Logging_parent        *string
 	Tests                 bazel.LabelListAttribute
+	Base_apex_name        *string
 }
 
 type convertedNativeSharedLibs struct {
diff --git a/bazel/cquery/request_type.go b/bazel/cquery/request_type.go
index e4830d3..118a3a9 100644
--- a/bazel/cquery/request_type.go
+++ b/bazel/cquery/request_type.go
@@ -33,6 +33,7 @@
 	TidyFiles            []string
 	TocFile              string
 	UnstrippedOutput     string
+	AbiDiffFiles         []string
 }
 
 type getOutputFilesRequestType struct{}
@@ -174,6 +175,11 @@
 if clang_tidy_info:
   tidy_files = [v.path for v in clang_tidy_info.tidy_files.to_list()]
 
+abi_diff_files = []
+abi_diff_info = p.get("//build/bazel/rules/abi:abi_dump.bzl%AbiDiffInfo")
+if abi_diff_info:
+  abi_diff_files = [f.path for f in abi_diff_info.diff_files.to_list()]
+
 return json_encode({
 	"OutputFiles": outputFiles,
 	"CcObjectFiles": ccObjectFiles,
@@ -187,6 +193,7 @@
 	"TidyFiles": tidy_files,
 	"TocFile": toc_file,
 	"UnstrippedOutput": unstripped,
+	"AbiDiffFiles": abi_diff_files,
 })`
 
 }
diff --git a/bp2build/apex_conversion_test.go b/bp2build/apex_conversion_test.go
index b6061e4..714b848 100644
--- a/bp2build/apex_conversion_test.go
+++ b/bp2build/apex_conversion_test.go
@@ -663,6 +663,7 @@
 		ExpectedBazelTargets: []string{
 			MakeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{
 				"android_manifest": `"ApogeeAndroidManifest.xml"`,
+				"base_apex_name":   `"com.android.apogee"`,
 				"binaries": `[
         ":cc_binary_1",
         ":sh_binary_2",
@@ -729,8 +730,9 @@
 `,
 		ExpectedBazelTargets: []string{
 			MakeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{
-				"file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`,
-				"manifest":      `"//a/b:apex_manifest.json"`,
+				"base_apex_name": `"com.android.apogee"`,
+				"file_contexts":  `"//system/sepolicy/apex:com.android.apogee-file_contexts"`,
+				"manifest":       `"//a/b:apex_manifest.json"`,
 			}),
 		}})
 }
@@ -763,8 +765,9 @@
 `,
 		ExpectedBazelTargets: []string{
 			MakeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{
-				"file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`,
-				"manifest":      `"//a/b:apogee_manifest.json"`,
+				"base_apex_name": `"com.android.apogee"`,
+				"file_contexts":  `"//system/sepolicy/apex:com.android.apogee-file_contexts"`,
+				"manifest":       `"//a/b:apogee_manifest.json"`,
 			}),
 		}})
 }
@@ -795,8 +798,9 @@
 `,
 		ExpectedBazelTargets: []string{
 			MakeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{
-				"file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`,
-				"manifest":      `"apex_manifest.json"`,
+				"base_apex_name": `"com.android.apogee"`,
+				"file_contexts":  `"//system/sepolicy/apex:com.android.apogee-file_contexts"`,
+				"manifest":       `"apex_manifest.json"`,
 			}),
 		}})
 }
@@ -828,8 +832,9 @@
 `,
 		ExpectedBazelTargets: []string{
 			MakeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{
-				"file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`,
-				"manifest":      `"apogee_manifest.json"`,
+				"base_apex_name": `"com.android.apogee"`,
+				"file_contexts":  `"//system/sepolicy/apex:com.android.apogee-file_contexts"`,
+				"manifest":       `"apogee_manifest.json"`,
 			}),
 		}})
 }
@@ -861,9 +866,10 @@
 `,
 		ExpectedBazelTargets: []string{
 			MakeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{
-				"file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`,
-				"manifest":      `"apex_manifest.json"`,
-				"package_name":  `"com.google.android.apogee"`,
+				"base_apex_name": `"com.android.apogee"`,
+				"file_contexts":  `"//system/sepolicy/apex:com.android.apogee-file_contexts"`,
+				"manifest":       `"apex_manifest.json"`,
+				"package_name":   `"com.google.android.apogee"`,
 			}),
 		}})
 }
@@ -900,9 +906,10 @@
 `,
 		ExpectedBazelTargets: []string{
 			MakeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{
-				"file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`,
-				"manifest":      `"apex_manifest.json"`,
-				"prebuilts":     `[":prebuilt_file"]`,
+				"base_apex_name": `"com.android.apogee"`,
+				"file_contexts":  `"//system/sepolicy/apex:com.android.apogee-file_contexts"`,
+				"manifest":       `"apex_manifest.json"`,
+				"prebuilts":      `[":prebuilt_file"]`,
 			}),
 		}})
 }
@@ -945,9 +952,10 @@
 `,
 		ExpectedBazelTargets: []string{
 			MakeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{
-				"file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`,
-				"manifest":      `"apex_manifest.json"`,
-				"prebuilts":     `[":prebuilt_file2"]`,
+				"base_apex_name": `"com.android.apogee"`,
+				"file_contexts":  `"//system/sepolicy/apex:com.android.apogee-file_contexts"`,
+				"manifest":       `"apex_manifest.json"`,
+				"prebuilts":      `[":prebuilt_file2"]`,
 			}),
 		}})
 }
@@ -985,9 +993,10 @@
 `,
 		ExpectedBazelTargets: []string{
 			MakeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{
-				"file_contexts": `"//system/sepolicy/apex:com.android.apogee-file_contexts"`,
-				"manifest":      `"apex_manifest.json"`,
-				"prebuilts":     `[]`,
+				"base_apex_name": `"com.android.apogee"`,
+				"file_contexts":  `"//system/sepolicy/apex:com.android.apogee-file_contexts"`,
+				"manifest":       `"apex_manifest.json"`,
+				"prebuilts":      `[]`,
 			}),
 		}})
 }
@@ -1019,6 +1028,7 @@
 `,
 		ExpectedBazelTargets: []string{
 			MakeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{
+				"base_apex_name": `"com.android.apogee"`,
 				"file_contexts":  `"//system/sepolicy/apex:com.android.apogee-file_contexts"`,
 				"manifest":       `"apex_manifest.json"`,
 				"logging_parent": `"foo.bar.baz"`,
@@ -1054,6 +1064,7 @@
 `,
 		ExpectedBazelTargets: []string{
 			MakeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{
+				"base_apex_name": `"com.android.apogee"`,
 				"file_contexts":  `"//system/sepolicy/apex:com.android.apogee-file_contexts"`,
 				"manifest":       `"apex_manifest.json"`,
 				"logging_parent": `"foo.bar.baz.override"`,
@@ -1099,8 +1110,9 @@
 `,
 		ExpectedBazelTargets: []string{
 			MakeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{
-				"file_contexts": `":com.android.apogee-file_contexts"`,
-				"manifest":      `"apogee_manifest.json"`,
+				"base_apex_name": `"com.android.apogee"`,
+				"file_contexts":  `":com.android.apogee-file_contexts"`,
+				"manifest":       `"apogee_manifest.json"`,
 			}),
 		}})
 }
@@ -1200,9 +1212,10 @@
 `,
 		ExpectedBazelTargets: []string{
 			MakeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{
-				"file_contexts": `":com.android.apogee-file_contexts"`,
-				"certificate":   `":com.google.android.apogee.certificate"`,
-				"manifest":      `"apogee_manifest.json"`,
+				"base_apex_name": `"com.android.apogee"`,
+				"file_contexts":  `":com.android.apogee-file_contexts"`,
+				"certificate":    `":com.google.android.apogee.certificate"`,
+				"manifest":       `"apogee_manifest.json"`,
 			}),
 		}})
 }
@@ -1244,6 +1257,7 @@
 `,
 		ExpectedBazelTargets: []string{
 			MakeBazelTarget("apex", "com.google.android.apogee", AttrNameToString{
+				"base_apex_name":   `"com.android.apogee"`,
 				"file_contexts":    `":com.android.apogee-file_contexts"`,
 				"certificate_name": `"com.google.android.apogee.certificate"`,
 				"manifest":         `"apogee_manifest.json"`,
diff --git a/bp2build/cc_library_conversion_test.go b/bp2build/cc_library_conversion_test.go
index edb0c43..a1e83d8 100644
--- a/bp2build/cc_library_conversion_test.go
+++ b/bp2build/cc_library_conversion_test.go
@@ -3645,3 +3645,49 @@
 		},
 	})
 }
+
+func TestCcLibraryApexAvailable(t *testing.T) {
+	runCcLibraryTestCase(t, Bp2buildTestCase{
+		Description:                "cc_library apex_available converted to tags",
+		ModuleTypeUnderTest:        "cc_library",
+		ModuleTypeUnderTestFactory: cc.LibraryFactory,
+		Blueprint: soongCcLibraryPreamble + `
+cc_library {
+    name: "a",
+    srcs: ["a.cpp"],
+    apex_available: ["com.android.foo"],
+}
+`,
+		ExpectedBazelTargets: makeCcLibraryTargets("a", AttrNameToString{
+			"tags":           `["apex_available=com.android.foo"]`,
+			"srcs":           `["a.cpp"]`,
+			"local_includes": `["."]`,
+		}),
+	},
+	)
+}
+
+func TestCcLibraryApexAvailableMultiple(t *testing.T) {
+	runCcLibraryTestCase(t, Bp2buildTestCase{
+		Description:                "cc_library apex_available converted to multiple tags",
+		ModuleTypeUnderTest:        "cc_library",
+		ModuleTypeUnderTestFactory: cc.LibraryFactory,
+		Blueprint: soongCcLibraryPreamble + `
+cc_library {
+    name: "a",
+    srcs: ["a.cpp"],
+    apex_available: ["com.android.foo", "//apex_available:platform", "com.android.bar"],
+}
+`,
+		ExpectedBazelTargets: makeCcLibraryTargets("a", AttrNameToString{
+			"tags": `[
+        "apex_available=com.android.foo",
+        "apex_available=//apex_available:platform",
+        "apex_available=com.android.bar",
+    ]`,
+			"srcs":           `["a.cpp"]`,
+			"local_includes": `["."]`,
+		}),
+	},
+	)
+}
diff --git a/bp2build/cc_prebuilt_library_conversion_test.go b/bp2build/cc_prebuilt_library_conversion_test.go
index 47006ac..2fe158e 100644
--- a/bp2build/cc_prebuilt_library_conversion_test.go
+++ b/bp2build/cc_prebuilt_library_conversion_test.go
@@ -91,9 +91,9 @@
 			ModuleTypeUnderTest:        "cc_prebuilt_library",
 			ModuleTypeUnderTestFactory: cc.PrebuiltLibraryFactory,
 			Filesystem: map[string]string{
-				"libf.so":    "",
-				"testdir/1/": "",
-				"testdir/2/": "",
+				"libf.so":             "",
+				"testdir/1/include.h": "",
+				"testdir/2/other.h":   "",
 			},
 			Blueprint: `
 cc_prebuilt_library {
diff --git a/bp2build/testing.go b/bp2build/testing.go
index 4e63d19..c059add 100644
--- a/bp2build/testing.go
+++ b/bp2build/testing.go
@@ -91,65 +91,66 @@
 
 func RunBp2BuildTestCase(t *testing.T, registerModuleTypes func(ctx android.RegistrationContext), tc Bp2buildTestCase) {
 	t.Helper()
-	bp2buildSetup := func(ctx *android.TestContext) {
-		registerModuleTypes(ctx)
-		ctx.RegisterForBazelConversion()
-	}
+	bp2buildSetup := android.GroupFixturePreparers(
+		android.FixtureRegisterWithContext(registerModuleTypes),
+		SetBp2BuildTestRunner,
+	)
 	runBp2BuildTestCaseWithSetup(t, bp2buildSetup, tc)
 }
 
 func RunApiBp2BuildTestCase(t *testing.T, registerModuleTypes func(ctx android.RegistrationContext), tc Bp2buildTestCase) {
 	t.Helper()
-	apiBp2BuildSetup := func(ctx *android.TestContext) {
-		registerModuleTypes(ctx)
-		ctx.RegisterForApiBazelConversion()
-	}
+	apiBp2BuildSetup := android.GroupFixturePreparers(
+		android.FixtureRegisterWithContext(registerModuleTypes),
+		SetApiBp2BuildTestRunner,
+	)
 	runBp2BuildTestCaseWithSetup(t, apiBp2BuildSetup, tc)
 }
 
-func runBp2BuildTestCaseWithSetup(t *testing.T, setup func(ctx *android.TestContext), tc Bp2buildTestCase) {
+func runBp2BuildTestCaseWithSetup(t *testing.T, extraPreparer android.FixturePreparer, tc Bp2buildTestCase) {
 	t.Helper()
 	dir := "."
 	filesystem := make(map[string][]byte)
-	toParse := []string{
-		"Android.bp",
-	}
 	for f, content := range tc.Filesystem {
-		if strings.HasSuffix(f, "Android.bp") {
-			toParse = append(toParse, f)
-		}
 		filesystem[f] = []byte(content)
 	}
-	config := android.TestConfig(buildDir, nil, tc.Blueprint, filesystem)
-	ctx := android.NewTestContext(config)
 
-	setup(ctx)
-	ctx.RegisterModuleType(tc.ModuleTypeUnderTest, tc.ModuleTypeUnderTestFactory)
-
-	// A default configuration for tests to not have to specify bp2build_available on top level targets.
-	bp2buildConfig := android.NewBp2BuildAllowlist().SetDefaultConfig(
-		allowlists.Bp2BuildConfig{
-			android.Bp2BuildTopLevel: allowlists.Bp2BuildDefaultTrueRecursively,
-		},
-	)
-	for _, f := range tc.KeepBuildFileForDirs {
-		bp2buildConfig.SetKeepExistingBuildFile(map[string]bool{
-			f: /*recursive=*/ false,
-		})
-	}
-	ctx.RegisterBp2BuildConfig(bp2buildConfig)
-
-	_, parseErrs := ctx.ParseFileList(dir, toParse)
-	if errored(t, tc, parseErrs) {
-		return
-	}
-	_, resolveDepsErrs := ctx.ResolveDependencies(config)
-	if errored(t, tc, resolveDepsErrs) {
-		return
+	preparers := []android.FixturePreparer{
+		extraPreparer,
+		android.FixtureMergeMockFs(filesystem),
+		android.FixtureWithRootAndroidBp(tc.Blueprint),
+		android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
+			ctx.RegisterModuleType(tc.ModuleTypeUnderTest, tc.ModuleTypeUnderTestFactory)
+		}),
+		android.FixtureModifyContext(func(ctx *android.TestContext) {
+			// A default configuration for tests to not have to specify bp2build_available on top level
+			// targets.
+			bp2buildConfig := android.NewBp2BuildAllowlist().SetDefaultConfig(
+				allowlists.Bp2BuildConfig{
+					android.Bp2BuildTopLevel: allowlists.Bp2BuildDefaultTrueRecursively,
+				},
+			)
+			for _, f := range tc.KeepBuildFileForDirs {
+				bp2buildConfig.SetKeepExistingBuildFile(map[string]bool{
+					f: /*recursive=*/ false,
+				})
+			}
+			ctx.RegisterBp2BuildConfig(bp2buildConfig)
+		}),
+		android.FixtureModifyEnv(func(env map[string]string) {
+			if tc.UnconvertedDepsMode == errorModulesUnconvertedDeps {
+				env["BP2BUILD_ERROR_UNCONVERTED"] = "true"
+			}
+		}),
 	}
 
-	parseAndResolveErrs := append(parseErrs, resolveDepsErrs...)
-	if tc.ExpectedErr != nil && checkError(t, parseAndResolveErrs, tc.ExpectedErr) {
+	preparer := android.GroupFixturePreparers(preparers...)
+	if tc.ExpectedErr != nil {
+		pattern := "\\Q" + tc.ExpectedErr.Error() + "\\E"
+		preparer = preparer.ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern(pattern))
+	}
+	result := preparer.RunTestWithCustomResult(t).(*BazelTestResult)
+	if len(result.Errs) > 0 {
 		return
 	}
 
@@ -157,27 +158,115 @@
 	if tc.Dir != "" {
 		checkDir = tc.Dir
 	}
-	codegenCtx := NewCodegenContext(config, ctx.Context, Bp2Build)
-	codegenCtx.unconvertedDepMode = tc.UnconvertedDepsMode
-	bazelTargets, errs := generateBazelTargetsForDir(codegenCtx, checkDir)
-	if tc.ExpectedErr != nil {
-		if checkError(t, errs, tc.ExpectedErr) {
-			return
-		} else {
-			t.Errorf("Expected error: %q, got: %q and %q", tc.ExpectedErr, errs, parseAndResolveErrs)
-		}
-	} else {
-		android.FailIfErrored(t, errs)
+	expectedTargets := map[string][]string{
+		checkDir: tc.ExpectedBazelTargets,
 	}
-	if actualCount, expectedCount := len(bazelTargets), len(tc.ExpectedBazelTargets); actualCount != expectedCount {
+
+	result.CompareAllBazelTargets(t, tc.Description, expectedTargets, true)
+}
+
+// SetBp2BuildTestRunner customizes the test fixture mechanism to run tests in Bp2Build mode.
+var SetBp2BuildTestRunner = android.FixtureSetTestRunner(&bazelTestRunner{Bp2Build})
+
+// SetApiBp2BuildTestRunner customizes the test fixture mechanism to run tests in ApiBp2build mode.
+var SetApiBp2BuildTestRunner = android.FixtureSetTestRunner(&bazelTestRunner{ApiBp2build})
+
+// bazelTestRunner customizes the test fixture mechanism to run tests of the bp2build and
+// apiBp2build build modes.
+type bazelTestRunner struct {
+	mode CodegenMode
+}
+
+func (b *bazelTestRunner) FinalPreparer(result *android.TestResult) android.CustomTestResult {
+	ctx := result.TestContext
+	switch b.mode {
+	case Bp2Build:
+		ctx.RegisterForBazelConversion()
+	case ApiBp2build:
+		ctx.RegisterForApiBazelConversion()
+	default:
+		panic(fmt.Errorf("unknown build mode: %d", b.mode))
+	}
+
+	return &BazelTestResult{TestResult: result}
+}
+
+func (b *bazelTestRunner) PostParseProcessor(result android.CustomTestResult) {
+	bazelResult := result.(*BazelTestResult)
+	ctx := bazelResult.TestContext
+	config := bazelResult.Config
+	_, errs := ctx.ResolveDependencies(config)
+	if bazelResult.CollateErrs(errs) {
+		return
+	}
+
+	codegenCtx := NewCodegenContext(config, ctx.Context, Bp2Build)
+	res, errs := GenerateBazelTargets(codegenCtx, false)
+	if bazelResult.CollateErrs(errs) {
+		return
+	}
+
+	// Store additional data for access by tests.
+	bazelResult.conversionResults = res
+}
+
+// BazelTestResult is a wrapper around android.TestResult to provide type safe access to the bazel
+// specific data stored by the bazelTestRunner.
+type BazelTestResult struct {
+	*android.TestResult
+
+	// The result returned by the GenerateBazelTargets function.
+	conversionResults
+}
+
+// CompareAllBazelTargets compares the BazelTargets produced by the test for all the directories
+// with the supplied set of expected targets.
+//
+// If ignoreUnexpected=false then this enforces an exact match where every BazelTarget produced must
+// have a corresponding expected BazelTarget.
+//
+// If ignoreUnexpected=true then it will ignore directories for which there are no expected targets.
+func (b BazelTestResult) CompareAllBazelTargets(t *testing.T, description string, expectedTargets map[string][]string, ignoreUnexpected bool) {
+	actualTargets := b.buildFileToTargets
+
+	// Generate the sorted set of directories to check.
+	dirsToCheck := android.SortedStringKeys(expectedTargets)
+	if !ignoreUnexpected {
+		// This needs to perform an exact match so add the directories in which targets were
+		// produced to the list of directories to check.
+		dirsToCheck = append(dirsToCheck, android.SortedStringKeys(actualTargets)...)
+		dirsToCheck = android.SortedUniqueStrings(dirsToCheck)
+	}
+
+	for _, dir := range dirsToCheck {
+		expected := expectedTargets[dir]
+		actual := actualTargets[dir]
+
+		if expected == nil {
+			if actual != nil {
+				t.Errorf("did not expect any bazel modules in %q but found %d", dir, len(actual))
+			}
+		} else if actual == nil {
+			expectedCount := len(expected)
+			if expectedCount > 0 {
+				t.Errorf("expected %d bazel modules in %q but did not find any", expectedCount, dir)
+			}
+		} else {
+			b.CompareBazelTargets(t, description, expected, actual)
+		}
+	}
+}
+
+func (b BazelTestResult) CompareBazelTargets(t *testing.T, description string, expectedContents []string, actualTargets BazelTargets) {
+	if actualCount, expectedCount := len(actualTargets), len(expectedContents); actualCount != expectedCount {
 		t.Errorf("%s: Expected %d bazel target (%s), got %d (%s)",
-			tc.Description, expectedCount, tc.ExpectedBazelTargets, actualCount, bazelTargets)
+			description, expectedCount, expectedContents, actualCount, actualTargets)
 	} else {
-		for i, target := range bazelTargets {
-			if w, g := tc.ExpectedBazelTargets[i], target.content; w != g {
+		for i, actualTarget := range actualTargets {
+			if w, g := expectedContents[i], actualTarget.content; w != g {
 				t.Errorf(
-					"%s: Expected generated Bazel target to be `%s`, got `%s`",
-					tc.Description, w, g)
+					"%s[%d]: Expected generated Bazel target to be `%s`, got `%s`",
+					description, i, w, g)
 			}
 		}
 	}
diff --git a/bpf/bpf.go b/bpf/bpf.go
index a840fa3..d91180b 100644
--- a/bpf/bpf.go
+++ b/bpf/bpf.go
@@ -227,7 +227,7 @@
 			for _, obj := range bpf.objs {
 				objName := name + "_" + obj.Base()
 				names = append(names, objName)
-				fmt.Fprintln(w, "include $(CLEAR_VARS)")
+				fmt.Fprintln(w, "include $(CLEAR_VARS)", " # bpf.bpf.obj")
 				fmt.Fprintln(w, "LOCAL_MODULE := ", objName)
 				data.Entries.WriteLicenseVariables(w)
 				fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", obj.String())
@@ -237,7 +237,7 @@
 				fmt.Fprintln(w, "include $(BUILD_PREBUILT)")
 				fmt.Fprintln(w)
 			}
-			fmt.Fprintln(w, "include $(CLEAR_VARS)")
+			fmt.Fprintln(w, "include $(CLEAR_VARS)", " # bpf.bpf")
 			fmt.Fprintln(w, "LOCAL_MODULE := ", name)
 			data.Entries.WriteLicenseVariables(w)
 			fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES :=", strings.Join(names, " "))
diff --git a/cc/androidmk.go b/cc/androidmk.go
index 58bb57c..ce35b5c 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -227,27 +227,17 @@
 	}
 }
 
-func (library *libraryDecorator) getAbiDiffsForAndroidMkDeps() []string {
-	if library.static() {
-		return nil
-	}
-	var abiDiffs []string
-	if library.sAbiDiff.Valid() {
-		abiDiffs = append(abiDiffs, library.sAbiDiff.String())
-	}
-	if library.prevSAbiDiff.Valid() {
-		abiDiffs = append(abiDiffs, library.prevSAbiDiff.String())
-	}
-	return abiDiffs
-}
-
 func (library *libraryDecorator) androidMkEntriesWriteAdditionalDependenciesForSourceAbiDiff(entries *android.AndroidMkEntries) {
-	entries.AddStrings("LOCAL_ADDITIONAL_DEPENDENCIES", library.getAbiDiffsForAndroidMkDeps()...)
+	if !library.static() {
+		entries.AddPaths("LOCAL_ADDITIONAL_DEPENDENCIES", library.sAbiDiff)
+	}
 }
 
 // TODO(ccross): remove this once apex/androidmk.go is converted to AndroidMkEntries
 func (library *libraryDecorator) androidMkWriteAdditionalDependenciesForSourceAbiDiff(w io.Writer) {
-	fmt.Fprintln(w, "LOCAL_ADDITIONAL_DEPENDENCIES +=", strings.Join(library.getAbiDiffsForAndroidMkDeps(), " "))
+	if !library.static() {
+		fmt.Fprintln(w, "LOCAL_ADDITIONAL_DEPENDENCIES +=", strings.Join(library.sAbiDiff.Strings(), " "))
+	}
 }
 
 func (library *libraryDecorator) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
@@ -540,8 +530,10 @@
 
 	entries.SubName = ""
 
-	if c.sanitizerProperties.CfiEnabled {
+	if c.isSanitizerEnabled(cfi) {
 		entries.SubName += ".cfi"
+	} else if c.isSanitizerEnabled(Hwasan) {
+		entries.SubName += ".hwasan"
 	}
 
 	entries.SubName += c.baseProperties.Androidmk_suffix
diff --git a/cc/binary.go b/cc/binary.go
index c2868e7..998934e 100644
--- a/cc/binary.go
+++ b/cc/binary.go
@@ -655,11 +655,12 @@
 	// shared with cc_test
 	binaryAttrs := binaryBp2buildAttrs(ctx, m)
 
+	tags := android.ApexAvailableTags(m)
 	ctx.CreateBazelTargetModule(bazel.BazelTargetModuleProperties{
 		Rule_class:        "cc_binary",
 		Bzl_load_location: "//build/bazel/rules/cc:cc_binary.bzl",
 	},
-		android.CommonAttributes{Name: m.Name()},
+		android.CommonAttributes{Name: m.Name(), Tags: tags},
 		&binaryAttrs)
 }
 
diff --git a/cc/builder.go b/cc/builder.go
index 46cea0b..0629406 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -292,12 +292,6 @@
 		},
 		"extraFlags", "referenceDump", "libName", "arch", "errorMessage")
 
-	// Rule to unzip a reference abi dump.
-	unzipRefSAbiDump = pctx.AndroidStaticRule("unzipRefSAbiDump",
-		blueprint.RuleParams{
-			Command: "gunzip -c $in > $out",
-		})
-
 	// Rule to zip files.
 	zip = pctx.AndroidStaticRule("zip",
 		blueprint.RuleParams{
@@ -911,59 +905,17 @@
 	return android.OptionalPathForPath(outputFile)
 }
 
-// unzipRefDump registers a build statement to unzip a reference abi dump.
-func unzipRefDump(ctx android.ModuleContext, zippedRefDump android.Path, baseName string) android.Path {
-	outputFile := android.PathForModuleOut(ctx, baseName+"_ref.lsdump")
-	ctx.Build(pctx, android.BuildParams{
-		Rule:        unzipRefSAbiDump,
-		Description: "gunzip" + outputFile.Base(),
-		Output:      outputFile,
-		Input:       zippedRefDump,
-	})
-	return outputFile
-}
-
-// sourceAbiDiff registers a build statement to compare linked sAbi dump files (.lsdump).
-func sourceAbiDiff(ctx android.ModuleContext, inputDump, referenceDump android.Path,
-	baseName string, diffFlags []string, prevVersion int,
-	checkAllApis, isLlndkOrNdk, isVndkExt, previousVersionDiff bool) android.OptionalPath {
+func transformAbiDumpToAbiDiff(ctx android.ModuleContext, inputDump, referenceDump android.Path,
+	baseName, nameExt string, extraFlags []string, errorMessage string) android.Path {
 
 	var outputFile android.ModuleOutPath
-	if previousVersionDiff {
-		outputFile = android.PathForModuleOut(ctx, baseName+"."+strconv.Itoa(prevVersion)+".abidiff")
+	if nameExt != "" {
+		outputFile = android.PathForModuleOut(ctx, baseName+"."+nameExt+".abidiff")
 	} else {
 		outputFile = android.PathForModuleOut(ctx, baseName+".abidiff")
 	}
 	libName := strings.TrimSuffix(baseName, filepath.Ext(baseName))
 
-	var extraFlags []string
-	if checkAllApis {
-		extraFlags = append(extraFlags, "-check-all-apis")
-	} else {
-		extraFlags = append(extraFlags,
-			"-allow-unreferenced-changes",
-			"-allow-unreferenced-elf-symbol-changes")
-	}
-
-	var errorMessage string
-	if previousVersionDiff {
-		errorMessage = "error: Please follow https://android.googlesource.com/platform/development/+/master/vndk/tools/header-checker/README.md#configure-cross_version-abi-check to resolve the ABI difference between your source code and version " + strconv.Itoa(prevVersion) + "."
-		sourceVersion := prevVersion + 1
-		extraFlags = append(extraFlags, "-target-version", strconv.Itoa(sourceVersion))
-	} else {
-		errorMessage = "error: Please update ABI references with: $$ANDROID_BUILD_TOP/development/vndk/tools/header-checker/utils/create_reference_dumps.py -l " + libName
-		extraFlags = append(extraFlags, "-target-version", "current")
-	}
-
-	if isLlndkOrNdk {
-		extraFlags = append(extraFlags, "-consider-opaque-types-different")
-	}
-	if isVndkExt || previousVersionDiff {
-		extraFlags = append(extraFlags, "-allow-extensions")
-	}
-	// TODO(b/232891473): Simplify the above logic with diffFlags.
-	extraFlags = append(extraFlags, diffFlags...)
-
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        sAbiDiff,
 		Description: "header-abi-diff " + outputFile.Base(),
@@ -978,7 +930,7 @@
 			"errorMessage":  errorMessage,
 		},
 	})
-	return android.OptionalPathForPath(outputFile)
+	return outputFile
 }
 
 // Generate a rule for extracting a table of contents from a shared library (.so)
diff --git a/cc/cc.go b/cc/cc.go
index 8b3f456..2ff5bba 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -2314,28 +2314,23 @@
 	return nonvariantLibs, variantLibs
 }
 
-func updateDepsWithApiImports(deps Deps, apiImports multitree.ApiImportInfo) Deps {
-	for idx, lib := range deps.SharedLibs {
-		deps.SharedLibs[idx] = GetReplaceModuleName(lib, apiImports.SharedLibs)
+func rewriteLibsForApiImports(c LinkableInterface, libs []string, replaceList map[string]string, config android.Config) ([]string, []string) {
+	nonVariantLibs := []string{}
+	variantLibs := []string{}
+
+	for _, lib := range libs {
+		replaceLibName := GetReplaceModuleName(lib, replaceList)
+		if replaceLibName == lib {
+			// Do not handle any libs which are not in API imports
+			nonVariantLibs = append(nonVariantLibs, replaceLibName)
+		} else if c.UseSdk() && inList(replaceLibName, *getNDKKnownLibs(config)) {
+			variantLibs = append(variantLibs, replaceLibName)
+		} else {
+			nonVariantLibs = append(nonVariantLibs, replaceLibName)
+		}
 	}
 
-	for idx, lib := range deps.LateSharedLibs {
-		deps.LateSharedLibs[idx] = GetReplaceModuleName(lib, apiImports.SharedLibs)
-	}
-
-	for idx, lib := range deps.RuntimeLibs {
-		deps.RuntimeLibs[idx] = GetReplaceModuleName(lib, apiImports.SharedLibs)
-	}
-
-	for idx, lib := range deps.SystemSharedLibs {
-		deps.SystemSharedLibs[idx] = GetReplaceModuleName(lib, apiImports.SharedLibs)
-	}
-
-	for idx, lib := range deps.ReexportSharedLibHeaders {
-		deps.ReexportSharedLibHeaders[idx] = GetReplaceModuleName(lib, apiImports.SharedLibs)
-	}
-
-	return deps
+	return nonVariantLibs, variantLibs
 }
 
 func (c *Module) DepsMutator(actx android.BottomUpMutatorContext) {
@@ -2354,8 +2349,15 @@
 	deps := c.deps(ctx)
 	apiImportInfo := GetApiImports(c, actx)
 
+	apiNdkLibs := []string{}
+	apiLateNdkLibs := []string{}
+
 	if ctx.Os() == android.Android && c.Target().NativeBridge != android.NativeBridgeEnabled {
-		deps = updateDepsWithApiImports(deps, apiImportInfo)
+		deps.SharedLibs, apiNdkLibs = rewriteLibsForApiImports(c, deps.SharedLibs, apiImportInfo.SharedLibs, ctx.Config())
+		deps.LateSharedLibs, apiLateNdkLibs = rewriteLibsForApiImports(c, deps.LateSharedLibs, apiImportInfo.SharedLibs, ctx.Config())
+		deps.SystemSharedLibs, _ = rewriteLibsForApiImports(c, deps.SystemSharedLibs, apiImportInfo.SharedLibs, ctx.Config())
+		deps.ReexportHeaderLibHeaders, _ = rewriteLibsForApiImports(c, deps.ReexportHeaderLibHeaders, apiImportInfo.SharedLibs, ctx.Config())
+		deps.ReexportSharedLibHeaders, _ = rewriteLibsForApiImports(c, deps.ReexportSharedLibHeaders, apiImportInfo.SharedLibs, ctx.Config())
 	}
 
 	c.Properties.AndroidMkSystemSharedLibs = deps.SystemSharedLibs
@@ -2542,12 +2544,20 @@
 		{Mutator: "version", Variation: version},
 		{Mutator: "link", Variation: "shared"},
 	}, ndkStubDepTag, variantNdkLibs...)
+	actx.AddVariationDependencies([]blueprint.Variation{
+		{Mutator: "version", Variation: version},
+		{Mutator: "link", Variation: "shared"},
+	}, ndkStubDepTag, apiNdkLibs...)
 
 	ndkLateStubDepTag := libraryDependencyTag{Kind: sharedLibraryDependency, Order: lateLibraryDependency, ndk: true, makeSuffix: "." + version}
 	actx.AddVariationDependencies([]blueprint.Variation{
 		{Mutator: "version", Variation: version},
 		{Mutator: "link", Variation: "shared"},
 	}, ndkLateStubDepTag, variantLateNdkLibs...)
+	actx.AddVariationDependencies([]blueprint.Variation{
+		{Mutator: "version", Variation: version},
+		{Mutator: "link", Variation: "shared"},
+	}, ndkLateStubDepTag, apiLateNdkLibs...)
 
 	if vndkdep := c.vndkdep; vndkdep != nil {
 		if vndkdep.isVndkExt() {
@@ -2601,6 +2611,10 @@
 		}
 		return
 	}
+	// TODO(b/244244438) : Remove this once all variants are implemented
+	if ccFrom, ok := from.(*Module); ok && ccFrom.isImportedApiLibrary() {
+		return
+	}
 	if from.SdkVersion() == "" {
 		// Platform code can link to anything
 		return
@@ -2627,6 +2641,10 @@
 			// the NDK.
 			return
 		}
+		if c.isImportedApiLibrary() {
+			// Imported library from the API surface is a stub library built against interface definition.
+			return
+		}
 	}
 
 	if strings.HasPrefix(ctx.ModuleName(), "libclang_rt.") && to.Module().Name() == "libc++" {
diff --git a/cc/config/global.go b/cc/config/global.go
index 8047d8e..61151d1 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -222,7 +222,6 @@
 		// http://b/145211066
 		"-Wno-implicit-int-float-conversion",
 		// New warnings to be fixed after clang-r377782.
-		"-Wno-sizeof-array-div",             // http://b/148815709
 		"-Wno-tautological-overlap-compare", // http://b/148815696
 		// New warnings to be fixed after clang-r383902.
 		"-Wno-deprecated-copy",                      // http://b/153746672
@@ -242,9 +241,15 @@
 		// New warnings to be fixed after clang-r468909
 		"-Wno-error=deprecated-builtins", // http://b/241601211
 		"-Wno-error=deprecated",          // in external/googletest/googletest
+		// New warnings to be fixed after clang-r475365
+		"-Wno-error=single-bit-bitfield-constant-conversion", // http://b/243965903
+		"-Wno-error=incompatible-function-pointer-types",     // http://b/257101299
+		"-Wno-error=enum-constexpr-conversion",               // http://b/243964282
 	}
 
 	noOverrideExternalGlobalCflags = []string{
+		// http://b/148815709
+		"-Wno-sizeof-array-div",
 		// http://b/197240255
 		"-Wno-unused-but-set-variable",
 		"-Wno-unused-but-set-parameter",
@@ -293,8 +298,6 @@
 	llvmNextExtraCommonGlobalCflags = []string{
 		// New warnings to be fixed after clang-r475365
 		"-Wno-error=single-bit-bitfield-constant-conversion", // http://b/243965903
-		// Skip deprecated flags.
-		"-Wno-unused-command-line-argument",
 	}
 
 	IllegalFlags = []string{
@@ -308,8 +311,8 @@
 
 	// prebuilts/clang default settings.
 	ClangDefaultBase         = "prebuilts/clang/host"
-	ClangDefaultVersion      = "clang-r468909b"
-	ClangDefaultShortVersion = "15.0.3"
+	ClangDefaultVersion      = "clang-r475365"
+	ClangDefaultShortVersion = "16.0.1"
 
 	// Directories with warnings from Android.bp files.
 	WarningAllowedProjects = []string{
@@ -350,6 +353,7 @@
 			// Default to zero initialization.
 			"-ftrivial-auto-var-init=zero",
 			"-enable-trivial-auto-var-init-zero-knowing-it-will-be-removed-from-clang",
+			"-Wno-unused-command-line-argument",
 		}...)
 	exportedVars.ExportStringList("CommonGlobalCflags", bazelCommonGlobalCflags)
 
@@ -360,14 +364,14 @@
 		// Automatically initialize any uninitialized stack variables.
 		// Prefer zero-init if multiple options are set.
 		if ctx.Config().IsEnvTrue("AUTO_ZERO_INITIALIZE") {
-			flags = append(flags, "-ftrivial-auto-var-init=zero -enable-trivial-auto-var-init-zero-knowing-it-will-be-removed-from-clang")
+			flags = append(flags, "-ftrivial-auto-var-init=zero -enable-trivial-auto-var-init-zero-knowing-it-will-be-removed-from-clang -Wno-unused-command-line-argument")
 		} else if ctx.Config().IsEnvTrue("AUTO_PATTERN_INITIALIZE") {
 			flags = append(flags, "-ftrivial-auto-var-init=pattern")
 		} else if ctx.Config().IsEnvTrue("AUTO_UNINITIALIZE") {
 			flags = append(flags, "-ftrivial-auto-var-init=uninitialized")
 		} else {
 			// Default to zero initialization.
-			flags = append(flags, "-ftrivial-auto-var-init=zero -enable-trivial-auto-var-init-zero-knowing-it-will-be-removed-from-clang")
+			flags = append(flags, "-ftrivial-auto-var-init=zero -enable-trivial-auto-var-init-zero-knowing-it-will-be-removed-from-clang -Wno-unused-command-line-argument")
 		}
 
 		// Workaround for ccache with clang.
diff --git a/cc/fuzz.go b/cc/fuzz.go
index 64bb7dd..3da7651 100644
--- a/cc/fuzz.go
+++ b/cc/fuzz.go
@@ -301,7 +301,6 @@
 	baseInstallerPath := "fuzz"
 
 	binary.baseInstaller = NewBaseInstaller(baseInstallerPath, baseInstallerPath, InstallInData)
-	module.sanitize.SetSanitizer(Fuzzer, true)
 
 	fuzzBin := &fuzzBinary{
 		binaryDecorator: binary,
@@ -315,7 +314,11 @@
 
 	// The fuzzer runtime is not present for darwin host modules, disable cc_fuzz modules when targeting darwin.
 	android.AddLoadHook(module, func(ctx android.LoadHookContext) {
-		disableDarwinAndLinuxBionic := struct {
+
+		extraProps := struct {
+			Sanitize struct {
+				Fuzzer *bool
+			}
 			Target struct {
 				Darwin struct {
 					Enabled *bool
@@ -325,9 +328,10 @@
 				}
 			}
 		}{}
-		disableDarwinAndLinuxBionic.Target.Darwin.Enabled = BoolPtr(false)
-		disableDarwinAndLinuxBionic.Target.Linux_bionic.Enabled = BoolPtr(false)
-		ctx.AppendProperties(&disableDarwinAndLinuxBionic)
+		extraProps.Sanitize.Fuzzer = BoolPtr(true)
+		extraProps.Target.Darwin.Enabled = BoolPtr(false)
+		extraProps.Target.Linux_bionic.Enabled = BoolPtr(false)
+		ctx.AppendProperties(&extraProps)
 
 		targetFramework := fuzz.GetFramework(ctx, fuzz.Cc)
 		if !fuzz.IsValidFrameworkForModule(targetFramework, fuzz.Cc, fuzzBin.fuzzPackagedModule.FuzzProperties.Fuzzing_frameworks) {
diff --git a/cc/genrule.go b/cc/genrule.go
index 4ef990c..d1c4c2a 100644
--- a/cc/genrule.go
+++ b/cc/genrule.go
@@ -48,6 +48,8 @@
 //
 //	CC_NATIVE_BRIDGE  the name of the subdirectory that native bridge libraries are stored in if
 //	                  the architecture has native bridge enabled, empty if it is disabled.
+//
+//	CC_OS             the name of the OS the command is being executed for.
 func GenRuleFactory() android.Module {
 	module := genrule.NewGenRule()
 
@@ -68,8 +70,9 @@
 func genruleCmdModifier(ctx android.ModuleContext, cmd string) string {
 	target := ctx.Target()
 	arch := target.Arch.ArchType
-	return fmt.Sprintf("CC_ARCH=%s CC_NATIVE_BRIDGE=%s CC_MULTILIB=%s && %s",
-		arch.Name, target.NativeBridgeRelativePath, arch.Multilib, cmd)
+	osName := target.Os.Name
+	return fmt.Sprintf("CC_ARCH=%s CC_NATIVE_BRIDGE=%s CC_MULTILIB=%s CC_OS=%s && %s",
+		arch.Name, target.NativeBridgeRelativePath, arch.Multilib, osName, cmd)
 }
 
 var _ android.ImageInterface = (*GenruleExtraProperties)(nil)
diff --git a/cc/library.go b/cc/library.go
index 673f1ca..d1d1945 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -448,11 +448,18 @@
 		Bzl_load_location: "//build/bazel/rules/cc:cc_library_shared.bzl",
 	}
 
+	tags := android.ApexAvailableTags(m)
 	ctx.CreateBazelTargetModuleWithRestrictions(staticProps,
-		android.CommonAttributes{Name: m.Name() + "_bp2build_cc_library_static"},
+		android.CommonAttributes{
+			Name: m.Name() + "_bp2build_cc_library_static",
+			Tags: tags,
+		},
 		staticTargetAttrs, staticAttrs.Enabled)
 	ctx.CreateBazelTargetModuleWithRestrictions(sharedProps,
-		android.CommonAttributes{Name: m.Name()},
+		android.CommonAttributes{
+			Name: m.Name(),
+			Tags: tags,
+		},
 		sharedTargetAttrs, sharedAttrs.Enabled)
 
 	createStubsBazelTargetIfNeeded(ctx, m, compilerAttrs, exportedIncludes, baseAttributes)
@@ -804,10 +811,7 @@
 	sAbiOutputFile android.OptionalPath
 
 	// Source Abi Diff
-	sAbiDiff android.OptionalPath
-
-	// Source Abi Diff against previous SDK version
-	prevSAbiDiff android.OptionalPath
+	sAbiDiff android.Paths
 
 	// Location of the static library in the sysroot. Empty if the library is
 	// not included in the NDK.
@@ -901,6 +905,10 @@
 	}
 	handler.module.linker.(*libraryDecorator).tocFile = tocFile
 
+	if len(ccInfo.AbiDiffFiles) > 0 {
+		handler.module.linker.(*libraryDecorator).sAbiDiff = android.PathsForBazelOut(ctx, ccInfo.AbiDiffFiles)
+	}
+
 	ctx.SetProvider(SharedLibraryInfoProvider, SharedLibraryInfo{
 		TableOfContents: tocFile,
 		SharedLibrary:   outputFilePath,
@@ -1797,10 +1805,8 @@
 	return library.coverageOutputFile
 }
 
-// pathForVndkRefAbiDump returns an OptionalPath representing the path of the
-// reference abi dump for the given module. This is not guaranteed to be valid.
-func pathForVndkRefAbiDump(ctx android.ModuleInstallPathContext, version, fileName string,
-	isNdk, isVndk, isGzip bool) android.OptionalPath {
+func getRefAbiDumpFile(ctx android.ModuleInstallPathContext,
+	versionedDumpDir, fileName string) android.OptionalPath {
 
 	currentArchType := ctx.Arch().ArchType
 	primaryArchType := ctx.Config().DevicePrimaryArchType()
@@ -1809,73 +1815,34 @@
 		archName += "_" + primaryArchType.String()
 	}
 
+	return android.ExistentPathForSource(ctx, versionedDumpDir, archName, "source-based",
+		fileName+".lsdump")
+}
+
+func getRefAbiDumpDir(isNdk, isVndk bool) string {
 	var dirName string
 	if isNdk {
 		dirName = "ndk"
 	} else if isVndk {
 		dirName = "vndk"
 	} else {
-		dirName = "platform" // opt-in libs
+		dirName = "platform"
 	}
-
-	binderBitness := ctx.DeviceConfig().BinderBitness()
-
-	var ext string
-	if isGzip {
-		ext = ".lsdump.gz"
-	} else {
-		ext = ".lsdump"
-	}
-
-	return android.ExistentPathForSource(ctx, "prebuilts", "abi-dumps", dirName,
-		version, binderBitness, archName, "source-based",
-		fileName+ext)
+	return filepath.Join("prebuilts", "abi-dumps", dirName)
 }
 
-func getRefAbiDumpFile(ctx ModuleContext, vndkVersion, fileName string) android.Path {
-	// The logic must be consistent with classifySourceAbiDump.
-	isNdk := ctx.isNdk(ctx.Config())
-	isVndk := ctx.useVndk() && ctx.isVndk()
-
-	refAbiDumpTextFile := pathForVndkRefAbiDump(ctx, vndkVersion, fileName, isNdk, isVndk, false)
-	refAbiDumpGzipFile := pathForVndkRefAbiDump(ctx, vndkVersion, fileName, isNdk, isVndk, true)
-
-	if refAbiDumpTextFile.Valid() {
-		if refAbiDumpGzipFile.Valid() {
-			ctx.ModuleErrorf(
-				"Two reference ABI dump files are found: %q and %q. Please delete the stale one.",
-				refAbiDumpTextFile, refAbiDumpGzipFile)
-			return nil
-		}
-		return refAbiDumpTextFile.Path()
-	}
-	if refAbiDumpGzipFile.Valid() {
-		return unzipRefDump(ctx, refAbiDumpGzipFile.Path(), fileName)
-	}
-	return nil
-}
-
-func prevDumpRefVersion(ctx ModuleContext) int {
+func prevRefAbiDumpVersion(ctx ModuleContext, dumpDir string) int {
 	sdkVersionInt := ctx.Config().PlatformSdkVersion().FinalInt()
 	sdkVersionStr := ctx.Config().PlatformSdkVersion().String()
 
 	if ctx.Config().PlatformSdkFinal() {
 		return sdkVersionInt - 1
 	} else {
-		var dirName string
-
-		isNdk := ctx.isNdk(ctx.Config())
-		if isNdk {
-			dirName = "ndk"
-		} else {
-			dirName = "platform"
-		}
-
 		// The platform SDK version can be upgraded before finalization while the corresponding abi dumps hasn't
 		// been generated. Thus the Cross-Version Check chooses PLATFORM_SDK_VERION - 1 as previous version.
 		// This situation could be identified by checking the existence of the PLATFORM_SDK_VERION dump directory.
-		refDumpDir := android.ExistentPathForSource(ctx, "prebuilts", "abi-dumps", dirName, sdkVersionStr)
-		if refDumpDir.Valid() {
+		versionedDumpDir := android.ExistentPathForSource(ctx, dumpDir, sdkVersionStr)
+		if versionedDumpDir.Valid() {
 			return sdkVersionInt
 		} else {
 			return sdkVersionInt - 1
@@ -1896,6 +1863,54 @@
 	}
 }
 
+// sourceAbiDiff registers a build statement to compare linked sAbi dump files (.lsdump).
+func (library *libraryDecorator) sourceAbiDiff(ctx android.ModuleContext, referenceDump android.Path,
+	baseName, nameExt string, isLlndkOrNdk, allowExtensions bool,
+	sourceVersion, errorMessage string) {
+
+	sourceDump := library.sAbiOutputFile.Path()
+
+	extraFlags := []string{"-target-version", sourceVersion}
+	if Bool(library.Properties.Header_abi_checker.Check_all_apis) {
+		extraFlags = append(extraFlags, "-check-all-apis")
+	} else {
+		extraFlags = append(extraFlags,
+			"-allow-unreferenced-changes",
+			"-allow-unreferenced-elf-symbol-changes")
+	}
+	if isLlndkOrNdk {
+		extraFlags = append(extraFlags, "-consider-opaque-types-different")
+	}
+	if allowExtensions {
+		extraFlags = append(extraFlags, "-allow-extensions")
+	}
+	extraFlags = append(extraFlags, library.Properties.Header_abi_checker.Diff_flags...)
+
+	library.sAbiDiff = append(
+		library.sAbiDiff,
+		transformAbiDumpToAbiDiff(ctx, sourceDump, referenceDump,
+			baseName, nameExt, extraFlags, errorMessage))
+}
+
+func (library *libraryDecorator) crossVersionAbiDiff(ctx android.ModuleContext, referenceDump android.Path,
+	baseName string, isLlndkOrNdk bool, sourceVersion, prevVersion string) {
+
+	errorMessage := "error: Please follow https://android.googlesource.com/platform/development/+/master/vndk/tools/header-checker/README.md#configure-cross_version-abi-check to resolve the ABI difference between your source code and version " + prevVersion + "."
+
+	library.sourceAbiDiff(ctx, referenceDump, baseName, prevVersion,
+		isLlndkOrNdk, /* allowExtensions */ true, sourceVersion, errorMessage)
+}
+
+func (library *libraryDecorator) sameVersionAbiDiff(ctx android.ModuleContext, referenceDump android.Path,
+	baseName string, isLlndkOrNdk, allowExtensions bool) {
+
+	libName := strings.TrimSuffix(baseName, filepath.Ext(baseName))
+	errorMessage := "error: Please update ABI references with: $$ANDROID_BUILD_TOP/development/vndk/tools/header-checker/utils/create_reference_dumps.py -l " + libName
+
+	library.sourceAbiDiff(ctx, referenceDump, baseName, /* nameExt */ "",
+		isLlndkOrNdk, allowExtensions, "current", errorMessage)
+}
+
 func (library *libraryDecorator) linkSAbiDumpFiles(ctx ModuleContext, objs Objects, fileName string, soFile android.Path) {
 	if library.sabi.shouldCreateSourceAbiDump() {
 		exportIncludeDirs := library.flagExporter.exportedIncludes(ctx)
@@ -1914,31 +1929,31 @@
 
 		addLsdumpPath(classifySourceAbiDump(ctx) + ":" + library.sAbiOutputFile.String())
 
+		// The logic must be consistent with classifySourceAbiDump.
 		isVndk := ctx.useVndk() && ctx.isVndk()
 		isNdk := ctx.isNdk(ctx.Config())
 		isLlndk := ctx.isImplementationForLLNDKPublic()
+		dumpDir := getRefAbiDumpDir(isNdk, isVndk)
+		binderBitness := ctx.DeviceConfig().BinderBitness()
 		// If NDK or PLATFORM library, check against previous version ABI.
 		if !isVndk {
-			prevVersion := prevDumpRefVersion(ctx)
-			prevRefAbiDumpFile := getRefAbiDumpFile(ctx, strconv.Itoa(prevVersion), fileName)
-			if prevRefAbiDumpFile != nil {
-				library.prevSAbiDiff = sourceAbiDiff(ctx, library.sAbiOutputFile.Path(),
-					prevRefAbiDumpFile, fileName,
-					library.Properties.Header_abi_checker.Diff_flags, prevVersion,
-					Bool(library.Properties.Header_abi_checker.Check_all_apis),
-					isLlndk || isNdk, ctx.IsVndkExt(), true)
+			prevVersionInt := prevRefAbiDumpVersion(ctx, dumpDir)
+			prevVersion := strconv.Itoa(prevVersionInt)
+			prevDumpDir := filepath.Join(dumpDir, prevVersion, binderBitness)
+			prevDumpFile := getRefAbiDumpFile(ctx, prevDumpDir, fileName)
+			if prevDumpFile.Valid() {
+				library.crossVersionAbiDiff(ctx, prevDumpFile.Path(),
+					fileName, isLlndk || isNdk,
+					strconv.Itoa(prevVersionInt+1), prevVersion)
 			}
 		}
-
+		// Check against the current version.
 		currVersion := currRefAbiDumpVersion(ctx, isVndk)
-		refAbiDumpFile := getRefAbiDumpFile(ctx, currVersion, fileName)
-		if refAbiDumpFile != nil {
-			library.sAbiDiff = sourceAbiDiff(ctx, library.sAbiOutputFile.Path(),
-				refAbiDumpFile, fileName,
-				library.Properties.Header_abi_checker.Diff_flags,
-				/* unused if not previousVersionDiff */ 0,
-				Bool(library.Properties.Header_abi_checker.Check_all_apis),
-				isLlndk || isNdk, ctx.IsVndkExt(), false)
+		currDumpDir := filepath.Join(dumpDir, currVersion, binderBitness)
+		currDumpFile := getRefAbiDumpFile(ctx, currDumpDir, fileName)
+		if currDumpFile.Valid() {
+			library.sameVersionAbiDiff(ctx, currDumpFile.Path(),
+				fileName, isLlndk || isNdk, ctx.IsVndkExt())
 		}
 	}
 }
@@ -2910,7 +2925,8 @@
 		Bzl_load_location: fmt.Sprintf("//build/bazel/rules/cc:%s.bzl", modType),
 	}
 
-	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: module.Name()}, attrs)
+	tags := android.ApexAvailableTags(module)
+	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: module.Name(), Tags: tags}, attrs)
 }
 
 // TODO(b/199902614): Can this be factored to share with the other Attributes?
diff --git a/cc/library_stub.go b/cc/library_stub.go
index 043c03c..c61e2d1 100644
--- a/cc/library_stub.go
+++ b/cc/library_stub.go
@@ -15,14 +15,17 @@
 package cc
 
 import (
+	"regexp"
 	"strings"
 
-	"github.com/google/blueprint/proptools"
-
 	"android/soong/android"
 	"android/soong/multitree"
 )
 
+var (
+	ndkVariantRegex = regexp.MustCompile("ndk\\.([a-zA-Z0-9]+)")
+)
+
 func init() {
 	RegisterLibraryStubBuildComponents(android.InitRegistrationContext)
 }
@@ -45,13 +48,17 @@
 	}
 
 	if m.UseVndk() && apiLibrary.hasLLNDKStubs() {
-		// Add LLNDK dependencies
-		for _, variant := range apiLibrary.properties.Variants {
-			if variant == "llndk" {
-				variantName := BuildApiVariantName(m.BaseModuleName(), "llndk", "")
-				ctx.AddDependency(m, nil, variantName)
-				break
-			}
+		// Add LLNDK variant dependency
+		if inList("llndk", apiLibrary.properties.Variants) {
+			variantName := BuildApiVariantName(m.BaseModuleName(), "llndk", "")
+			ctx.AddDependency(m, nil, variantName)
+		}
+	} else if m.IsSdkVariant() {
+		// Add NDK variant dependencies
+		targetVariant := "ndk." + m.StubsVersion()
+		if inList(targetVariant, apiLibrary.properties.Variants) {
+			variantName := BuildApiVariantName(m.BaseModuleName(), targetVariant, "")
+			ctx.AddDependency(m, nil, variantName)
 		}
 	}
 }
@@ -117,12 +124,31 @@
 	}
 }
 
+func (d *apiLibraryDecorator) linkerInit(ctx BaseModuleContext) {
+	d.baseLinker.linkerInit(ctx)
+
+	if d.hasNDKStubs() {
+		// Set SDK version of module as current
+		ctx.Module().(*Module).Properties.Sdk_version = StringPtr("current")
+
+		// Add NDK stub as NDK known libs
+		name := ctx.ModuleName()
+
+		ndkKnownLibsLock.Lock()
+		ndkKnownLibs := getNDKKnownLibs(ctx.Config())
+		if !inList(name, *ndkKnownLibs) {
+			*ndkKnownLibs = append(*ndkKnownLibs, name)
+		}
+		ndkKnownLibsLock.Unlock()
+	}
+}
+
 func (d *apiLibraryDecorator) link(ctx ModuleContext, flags Flags, deps PathDeps, objects Objects) android.Path {
 	m, _ := ctx.Module().(*Module)
 
 	var in android.Path
 
-	if src := proptools.String(d.properties.Src); src != "" {
+	if src := String(d.properties.Src); src != "" {
 		in = android.PathForModuleSrc(ctx, src)
 	}
 
@@ -146,10 +172,10 @@
 				// Copy LLDNK properties to cc_api_library module
 				d.libraryDecorator.flagExporter.Properties.Export_include_dirs = append(
 					d.libraryDecorator.flagExporter.Properties.Export_include_dirs,
-					variantMod.exportProperties.Export_headers...)
+					variantMod.exportProperties.Export_include_dirs...)
 
 				// Export headers as system include dirs if specified. Mostly for libc
-				if proptools.Bool(variantMod.exportProperties.Export_headers_as_system) {
+				if Bool(variantMod.exportProperties.Export_headers_as_system) {
 					d.libraryDecorator.flagExporter.Properties.Export_system_include_dirs = append(
 						d.libraryDecorator.flagExporter.Properties.Export_system_include_dirs,
 						d.libraryDecorator.flagExporter.Properties.Export_include_dirs...)
@@ -157,6 +183,29 @@
 				}
 			}
 		}
+	} else if m.IsSdkVariant() {
+		// NDK Variant
+		apiVariantModule := BuildApiVariantName(m.BaseModuleName(), "ndk", m.StubsVersion())
+
+		var mod android.Module
+
+		ctx.VisitDirectDeps(func(depMod android.Module) {
+			if depMod.Name() == apiVariantModule {
+				mod = depMod
+			}
+		})
+
+		if mod != nil {
+			variantMod, ok := mod.(*CcApiVariant)
+			if ok {
+				in = variantMod.Src()
+
+				// Copy NDK properties to cc_api_library module
+				d.libraryDecorator.flagExporter.Properties.Export_include_dirs = append(
+					d.libraryDecorator.flagExporter.Properties.Export_include_dirs,
+					variantMod.exportProperties.Export_include_dirs...)
+			}
+		}
 	}
 
 	// Flags reexported from dependencies. (e.g. vndk_prebuilt_shared)
@@ -214,6 +263,14 @@
 
 	// TODO(b/244244438) Create more version information for NDK and APEX variations
 	// NDK variants
+
+	if m.IsSdkVariant() {
+		// TODO(b/249193999) Do not check if module has NDK stubs once all NDK cc_api_library contains ndk variant of cc_api_variant.
+		if d.hasNDKStubs() {
+			return d.getNdkVersions()
+		}
+	}
+
 	if m.MinSdkVersion() == "" {
 		return nil
 	}
@@ -229,14 +286,30 @@
 }
 
 func (d *apiLibraryDecorator) hasLLNDKStubs() bool {
+	return inList("llndk", d.properties.Variants)
+}
+
+func (d *apiLibraryDecorator) hasNDKStubs() bool {
 	for _, variant := range d.properties.Variants {
-		if strings.Contains(variant, "llndk") {
+		if ndkVariantRegex.MatchString(variant) {
 			return true
 		}
 	}
 	return false
 }
 
+func (d *apiLibraryDecorator) getNdkVersions() []string {
+	ndkVersions := []string{}
+
+	for _, variant := range d.properties.Variants {
+		if match := ndkVariantRegex.FindStringSubmatch(variant); len(match) == 2 {
+			ndkVersions = append(ndkVersions, match[1])
+		}
+	}
+
+	return ndkVersions
+}
+
 // 'cc_api_headers' is similar with 'cc_api_library', but which replaces
 // header libraries. The module will replace any dependencies to existing
 // original header libraries.
@@ -289,7 +362,7 @@
 
 type variantExporterProperties struct {
 	// Header directory to export
-	Export_headers []string `android:"arch_variant"`
+	Export_include_dirs []string `android:"arch_variant"`
 
 	// Export all headers as system include
 	Export_headers_as_system *bool
@@ -320,18 +393,18 @@
 func (v *CcApiVariant) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	// No need to build
 
-	if proptools.String(v.properties.Src) == "" {
+	if String(v.properties.Src) == "" {
 		ctx.PropertyErrorf("src", "src is a required property")
 	}
 
 	// Skip the existence check of the stub prebuilt file.
 	// The file is not guaranteed to exist during Soong analysis.
 	// Build orchestrator will be responsible for creating a connected ninja graph.
-	v.src = android.MaybeExistentPathForSource(ctx, ctx.ModuleDir(), proptools.String(v.properties.Src))
+	v.src = android.MaybeExistentPathForSource(ctx, ctx.ModuleDir(), String(v.properties.Src))
 }
 
 func (v *CcApiVariant) Name() string {
-	version := proptools.String(v.properties.Version)
+	version := String(v.properties.Version)
 	return BuildApiVariantName(v.BaseModuleName(), *v.properties.Variant, version)
 }
 
@@ -349,8 +422,10 @@
 }
 
 // Implement ImageInterface to generate image variants
-func (v *CcApiVariant) ImageMutatorBegin(ctx android.BaseModuleContext)               {}
-func (v *CcApiVariant) CoreVariantNeeded(ctx android.BaseModuleContext) bool          { return false }
+func (v *CcApiVariant) ImageMutatorBegin(ctx android.BaseModuleContext) {}
+func (v *CcApiVariant) CoreVariantNeeded(ctx android.BaseModuleContext) bool {
+	return String(v.properties.Variant) == "ndk"
+}
 func (v *CcApiVariant) RamdiskVariantNeeded(ctx android.BaseModuleContext) bool       { return false }
 func (v *CcApiVariant) VendorRamdiskVariantNeeded(ctx android.BaseModuleContext) bool { return false }
 func (v *CcApiVariant) DebugRamdiskVariantNeeded(ctx android.BaseModuleContext) bool  { return false }
@@ -359,7 +434,7 @@
 	var variations []string
 	platformVndkVersion := ctx.DeviceConfig().PlatformVndkVersion()
 
-	if proptools.String(v.properties.Variant) == "llndk" {
+	if String(v.properties.Variant) == "llndk" {
 		variations = append(variations, VendorVariationPrefix+platformVndkVersion)
 		variations = append(variations, ProductVariationPrefix+platformVndkVersion)
 	}
diff --git a/cc/library_stub_test.go b/cc/library_stub_test.go
index 8ce74c4..868447a 100644
--- a/cc/library_stub_test.go
+++ b/cc/library_stub_test.go
@@ -308,7 +308,7 @@
 			name: "libbar",
 			variant: "llndk",
 			src: "libbar_llndk.so",
-			export_headers: ["libbar_llndk_include"]
+			export_include_dirs: ["libbar_llndk_include"]
 		}
 
 		api_imports {
@@ -322,16 +322,188 @@
 
 	ctx := prepareForCcTest.RunTestWithBp(t, bp)
 
-	libfoo := ctx.ModuleForTests("binfoo", "android_vendor.29_arm64_armv8-a").Module()
+	binfoo := ctx.ModuleForTests("binfoo", "android_vendor.29_arm64_armv8-a").Module()
 	libbarApiImport := ctx.ModuleForTests("libbar.apiimport", "android_vendor.29_arm64_armv8-a_shared").Module()
 	libbarApiVariant := ctx.ModuleForTests("libbar.llndk.apiimport", "android_vendor.29_arm64_armv8-a").Module()
 
-	android.AssertBoolEquals(t, "Stub library from API surface should be linked", true, hasDirectDependency(t, ctx, libfoo, libbarApiImport))
+	android.AssertBoolEquals(t, "Stub library from API surface should be linked", true, hasDirectDependency(t, ctx, binfoo, libbarApiImport))
 	android.AssertBoolEquals(t, "Stub library variant from API surface should be linked", true, hasDirectDependency(t, ctx, libbarApiImport, libbarApiVariant))
 
-	libFooLibFlags := ctx.ModuleForTests("binfoo", "android_vendor.29_arm64_armv8-a").Rule("ld").Args["libFlags"]
-	android.AssertStringDoesContain(t, "Vendor binary should be linked with LLNDK variant source", libFooLibFlags, "libbar_llndk.so")
+	binFooLibFlags := ctx.ModuleForTests("binfoo", "android_vendor.29_arm64_armv8-a").Rule("ld").Args["libFlags"]
+	android.AssertStringDoesContain(t, "Vendor binary should be linked with LLNDK variant source", binFooLibFlags, "libbar_llndk.so")
 
-	libFooCFlags := ctx.ModuleForTests("binfoo", "android_vendor.29_arm64_armv8-a").Rule("cc").Args["cFlags"]
-	android.AssertStringDoesContain(t, "Vendor binary should include headers from the LLNDK variant source", libFooCFlags, "-Ilibbar_llndk_include")
+	binFooCFlags := ctx.ModuleForTests("binfoo", "android_vendor.29_arm64_armv8-a").Rule("cc").Args["cFlags"]
+	android.AssertStringDoesContain(t, "Vendor binary should include headers from the LLNDK variant source", binFooCFlags, "-Ilibbar_llndk_include")
+}
+
+func TestApiLibraryWithNdkVariant(t *testing.T) {
+	bp := `
+		cc_binary {
+			name: "binfoo",
+			sdk_version: "29",
+			srcs: ["binfoo.cc"],
+			shared_libs: ["libbar"],
+			stl: "c++_shared",
+		}
+
+		cc_binary {
+			name: "binbaz",
+			sdk_version: "30",
+			srcs: ["binbaz.cc"],
+			shared_libs: ["libbar"],
+			stl: "c++_shared",
+		}
+
+		cc_api_library {
+			name: "libbar",
+			// TODO(b/244244438) Remove src property once all variants are implemented.
+			src: "libbar.so",
+			variants: [
+				"ndk.29",
+				"ndk.30",
+				"ndk.current",
+			],
+		}
+
+		cc_api_variant {
+			name: "libbar",
+			variant: "ndk",
+			version: "29",
+			src: "libbar_ndk_29.so",
+			export_include_dirs: ["libbar_ndk_29_include"]
+		}
+
+		cc_api_variant {
+			name: "libbar",
+			variant: "ndk",
+			version: "30",
+			src: "libbar_ndk_30.so",
+			export_include_dirs: ["libbar_ndk_30_include"]
+		}
+
+		cc_api_variant {
+			name: "libbar",
+			variant: "ndk",
+			version: "current",
+			src: "libbar_ndk_current.so",
+			export_include_dirs: ["libbar_ndk_current_include"]
+		}
+
+		api_imports {
+			name: "api_imports",
+			shared_libs: [
+				"libbar",
+			],
+			header_libs: [],
+		}
+	`
+
+	ctx := prepareForCcTest.RunTestWithBp(t, bp)
+
+	binfoo := ctx.ModuleForTests("binfoo", "android_arm64_armv8-a_sdk").Module()
+	libbarApiImportv29 := ctx.ModuleForTests("libbar.apiimport", "android_arm64_armv8-a_sdk_shared_29").Module()
+	libbarApiVariantv29 := ctx.ModuleForTests("libbar.ndk.29.apiimport", "android_arm64_armv8-a_sdk").Module()
+	libbarApiImportv30 := ctx.ModuleForTests("libbar.apiimport", "android_arm64_armv8-a_sdk_shared_30").Module()
+	libbarApiVariantv30 := ctx.ModuleForTests("libbar.ndk.30.apiimport", "android_arm64_armv8-a_sdk").Module()
+
+	android.AssertBoolEquals(t, "Stub library from API surface should be linked with target version", true, hasDirectDependency(t, ctx, binfoo, libbarApiImportv29))
+	android.AssertBoolEquals(t, "Stub library variant from API surface should be linked with target version", true, hasDirectDependency(t, ctx, libbarApiImportv29, libbarApiVariantv29))
+	android.AssertBoolEquals(t, "Stub library from API surface should not be linked with different version", false, hasDirectDependency(t, ctx, binfoo, libbarApiImportv30))
+	android.AssertBoolEquals(t, "Stub library variant from API surface should not be linked with different version", false, hasDirectDependency(t, ctx, libbarApiImportv29, libbarApiVariantv30))
+
+	binbaz := ctx.ModuleForTests("binbaz", "android_arm64_armv8-a_sdk").Module()
+
+	android.AssertBoolEquals(t, "Stub library from API surface should be linked with target version", true, hasDirectDependency(t, ctx, binbaz, libbarApiImportv30))
+	android.AssertBoolEquals(t, "Stub library from API surface should not be linked with different version", false, hasDirectDependency(t, ctx, binbaz, libbarApiImportv29))
+
+	binFooLibFlags := ctx.ModuleForTests("binfoo", "android_arm64_armv8-a_sdk").Rule("ld").Args["libFlags"]
+	android.AssertStringDoesContain(t, "Binary using sdk should be linked with NDK variant source", binFooLibFlags, "libbar_ndk_29.so")
+
+	binFooCFlags := ctx.ModuleForTests("binfoo", "android_arm64_armv8-a_sdk").Rule("cc").Args["cFlags"]
+	android.AssertStringDoesContain(t, "Binary using sdk should include headers from the NDK variant source", binFooCFlags, "-Ilibbar_ndk_29_include")
+}
+
+func TestApiLibraryWithMultipleVariants(t *testing.T) {
+	bp := `
+		cc_binary {
+			name: "binfoo",
+			sdk_version: "29",
+			srcs: ["binfoo.cc"],
+			shared_libs: ["libbar"],
+			stl: "c++_shared",
+		}
+
+		cc_binary {
+			name: "binbaz",
+			vendor: true,
+			srcs: ["binbaz.cc"],
+			shared_libs: ["libbar"],
+		}
+
+		cc_api_library {
+			name: "libbar",
+			// TODO(b/244244438) Remove src property once all variants are implemented.
+			src: "libbar.so",
+			vendor_available: true,
+			variants: [
+				"llndk",
+				"ndk.29",
+				"ndk.30",
+				"ndk.current",
+			],
+		}
+
+		cc_api_variant {
+			name: "libbar",
+			variant: "ndk",
+			version: "29",
+			src: "libbar_ndk_29.so",
+			export_include_dirs: ["libbar_ndk_29_include"]
+		}
+
+		cc_api_variant {
+			name: "libbar",
+			variant: "ndk",
+			version: "30",
+			src: "libbar_ndk_30.so",
+			export_include_dirs: ["libbar_ndk_30_include"]
+		}
+
+		cc_api_variant {
+			name: "libbar",
+			variant: "ndk",
+			version: "current",
+			src: "libbar_ndk_current.so",
+			export_include_dirs: ["libbar_ndk_current_include"]
+		}
+
+		cc_api_variant {
+			name: "libbar",
+			variant: "llndk",
+			src: "libbar_llndk.so",
+			export_include_dirs: ["libbar_llndk_include"]
+		}
+
+		api_imports {
+			name: "api_imports",
+			shared_libs: [
+				"libbar",
+			],
+			header_libs: [],
+		}
+	`
+	ctx := prepareForCcTest.RunTestWithBp(t, bp)
+
+	binfoo := ctx.ModuleForTests("binfoo", "android_arm64_armv8-a_sdk").Module()
+	libbarApiImportv29 := ctx.ModuleForTests("libbar.apiimport", "android_arm64_armv8-a_sdk_shared_29").Module()
+	libbarApiImportLlndk := ctx.ModuleForTests("libbar.apiimport", "android_vendor.29_arm64_armv8-a_shared").Module()
+
+	android.AssertBoolEquals(t, "Binary using SDK should be linked with API library from NDK variant", true, hasDirectDependency(t, ctx, binfoo, libbarApiImportv29))
+	android.AssertBoolEquals(t, "Binary using SDK should not be linked with API library from LLNDK variant", false, hasDirectDependency(t, ctx, binfoo, libbarApiImportLlndk))
+
+	binbaz := ctx.ModuleForTests("binbaz", "android_vendor.29_arm64_armv8-a").Module()
+
+	android.AssertBoolEquals(t, "Vendor binary should be linked with API library from LLNDK variant", true, hasDirectDependency(t, ctx, binbaz, libbarApiImportLlndk))
+	android.AssertBoolEquals(t, "Vendor binary should not be linked with API library from NDK variant", false, hasDirectDependency(t, ctx, binbaz, libbarApiImportv29))
+
 }
diff --git a/cc/prebuilt.go b/cc/prebuilt.go
index 1842e5a..9fbf879 100644
--- a/cc/prebuilt.go
+++ b/cc/prebuilt.go
@@ -388,7 +388,9 @@
 	if fullBuild {
 		name += "_bp2build_cc_library_static"
 	}
-	ctx.CreateBazelTargetModuleWithRestrictions(props, android.CommonAttributes{Name: name}, attrs, prebuiltAttrs.Enabled)
+
+	tags := android.ApexAvailableTags(module)
+	ctx.CreateBazelTargetModuleWithRestrictions(props, android.CommonAttributes{Name: name, Tags: tags}, attrs, prebuiltAttrs.Enabled)
 }
 
 type bazelPrebuiltLibrarySharedAttributes struct {
@@ -408,7 +410,8 @@
 	}
 
 	name := android.RemoveOptionalPrebuiltPrefix(module.Name())
-	ctx.CreateBazelTargetModuleWithRestrictions(props, android.CommonAttributes{Name: name}, attrs, prebuiltAttrs.Enabled)
+	tags := android.ApexAvailableTags(module)
+	ctx.CreateBazelTargetModuleWithRestrictions(props, android.CommonAttributes{Name: name, Tags: tags}, attrs, prebuiltAttrs.Enabled)
 }
 
 type prebuiltObjectProperties struct {
@@ -740,7 +743,8 @@
 	}
 
 	name := android.RemoveOptionalPrebuiltPrefix(module.Name())
-	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: name}, attrs)
+	tags := android.ApexAvailableTags(module)
+	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: name, Tags: tags}, attrs)
 }
 
 type Sanitized struct {
@@ -759,10 +763,10 @@
 	if sanitize == nil {
 		return nil
 	}
-	if Bool(sanitize.Properties.Sanitize.Address) && sanitized.Address.Srcs != nil {
+	if sanitize.isSanitizerEnabled(Asan) && sanitized.Address.Srcs != nil {
 		return sanitized.Address.Srcs
 	}
-	if Bool(sanitize.Properties.Sanitize.Hwaddress) && sanitized.Hwaddress.Srcs != nil {
+	if sanitize.isSanitizerEnabled(Hwasan) && sanitized.Hwaddress.Srcs != nil {
 		return sanitized.Hwaddress.Srcs
 	}
 	return sanitized.None.Srcs
diff --git a/cc/proto.go b/cc/proto.go
index cf5ed04..27f37cb 100644
--- a/cc/proto.go
+++ b/cc/proto.go
@@ -205,13 +205,13 @@
 	protoAttrs.Deps.SetValue(protoInfo.Proto_libs)
 
 	name := m.Name() + suffix
-
+	tags := android.ApexAvailableTags(m)
 	ctx.CreateBazelTargetModule(
 		bazel.BazelTargetModuleProperties{
 			Rule_class:        rule_class,
 			Bzl_load_location: "//build/bazel/rules/cc:cc_proto.bzl",
 		},
-		android.CommonAttributes{Name: name},
+		android.CommonAttributes{Name: name, Tags: tags},
 		&protoAttrs)
 
 	var privateHdrs bool
diff --git a/cc/sanitize.go b/cc/sanitize.go
index d3fc221..eba709b 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -171,6 +171,20 @@
 	}
 }
 
+// shouldPropagateToSharedLibraryDeps returns whether a sanitizer type should propagate to share
+// dependencies. In most cases, sanitizers only propagate to static dependencies; however, some
+// sanitizers also must be enabled for shared libraries for linking.
+func (t SanitizerType) shouldPropagateToSharedLibraryDeps() bool {
+	switch t {
+	case Fuzzer:
+		// Typically, shared libs are not split. However, for fuzzer, we split even for shared libs
+		// because a library sanitized for fuzzer can't be linked from a library that isn't sanitized
+		// for fuzzer.
+		return true
+	default:
+		return false
+	}
+}
 func (*Module) SanitizerSupported(t SanitizerType) bool {
 	switch t {
 	case Asan:
@@ -286,15 +300,72 @@
 	Blocklist *string
 }
 
+type sanitizeMutatedProperties struct {
+	// Whether sanitizers can be enabled on this module
+	Never *bool `blueprint:"mutated"`
+
+	// Whether ASan (Address sanitizer) is enabled for this module.
+	// Hwaddress sanitizer takes precedence over this sanitizer.
+	Address *bool `blueprint:"mutated"`
+	// Whether TSan (Thread sanitizer) is enabled for this module
+	Thread *bool `blueprint:"mutated"`
+	// Whether HWASan (Hardware Address sanitizer) is enabled for this module
+	Hwaddress *bool `blueprint:"mutated"`
+
+	// Whether Undefined behavior sanitizer is enabled for this module
+	All_undefined *bool `blueprint:"mutated"`
+	// Whether undefined behavior sanitizer subset is enabled for this module
+	Undefined *bool `blueprint:"mutated"`
+	// List of specific undefined behavior sanitizers enabled for this module
+	Misc_undefined []string `blueprint:"mutated"`
+	// Whether Fuzzeris enabled for this module
+	Fuzzer *bool `blueprint:"mutated"`
+	// whether safe-stack sanitizer is enabled for this module
+	Safestack *bool `blueprint:"mutated"`
+	// Whether cfi sanitizer is enabled for this module
+	Cfi *bool `blueprint:"mutated"`
+	// Whether signed/unsigned integer overflow sanitizer is enabled for this module
+	Integer_overflow *bool `blueprint:"mutated"`
+	// Whether scudo sanitizer is enabled for this module
+	Scudo *bool `blueprint:"mutated"`
+	// Whether shadow-call-stack sanitizer is enabled for this module.
+	Scs *bool `blueprint:"mutated"`
+	// Whether Memory-tagging is enabled for this module
+	Memtag_heap *bool `blueprint:"mutated"`
+	// Whether Memory-tagging stack instrumentation is enabled for this module
+	Memtag_stack *bool `blueprint:"mutated"`
+
+	// Whether a modifier for ASAN and HWASAN for write only instrumentation is enabled for this
+	// module
+	Writeonly *bool `blueprint:"mutated"`
+
+	// Sanitizers to run in the diagnostic mode (as opposed to the release mode).
+	Diag struct {
+		// Whether Undefined behavior sanitizer, diagnostic mode is enabled for this module
+		Undefined *bool `blueprint:"mutated"`
+		// Whether cfi sanitizer, diagnostic mode is enabled for this module
+		Cfi *bool `blueprint:"mutated"`
+		// Whether signed/unsigned integer overflow sanitizer, diagnostic mode is enabled for this
+		// module
+		Integer_overflow *bool `blueprint:"mutated"`
+		// Whether Memory-tagging, diagnostic mode is enabled for this module
+		Memtag_heap *bool `blueprint:"mutated"`
+		// List of specific undefined behavior sanitizers enabled in diagnostic mode
+		Misc_undefined []string `blueprint:"mutated"`
+	} `blueprint:"mutated"`
+}
+
 type SanitizeProperties struct {
-	Sanitize          SanitizeUserProps `android:"arch_variant"`
-	SanitizerEnabled  bool              `blueprint:"mutated"`
-	MinimalRuntimeDep bool              `blueprint:"mutated"`
-	BuiltinsDep       bool              `blueprint:"mutated"`
-	UbsanRuntimeDep   bool              `blueprint:"mutated"`
-	InSanitizerDir    bool              `blueprint:"mutated"`
-	Sanitizers        []string          `blueprint:"mutated"`
-	DiagSanitizers    []string          `blueprint:"mutated"`
+	Sanitize        SanitizeUserProps         `android:"arch_variant"`
+	SanitizeMutated sanitizeMutatedProperties `blueprint:"mutated"`
+
+	SanitizerEnabled  bool     `blueprint:"mutated"`
+	MinimalRuntimeDep bool     `blueprint:"mutated"`
+	BuiltinsDep       bool     `blueprint:"mutated"`
+	UbsanRuntimeDep   bool     `blueprint:"mutated"`
+	InSanitizerDir    bool     `blueprint:"mutated"`
+	Sanitizers        []string `blueprint:"mutated"`
+	DiagSanitizers    []string `blueprint:"mutated"`
 }
 
 type sanitize struct {
@@ -317,8 +388,42 @@
 	return []interface{}{&sanitize.Properties}
 }
 
+func (p *sanitizeMutatedProperties) copyUserPropertiesToMutated(userProps *SanitizeUserProps) {
+	p.Never = userProps.Never
+	p.Address = userProps.Address
+	p.All_undefined = userProps.All_undefined
+	p.Cfi = userProps.Cfi
+	p.Fuzzer = userProps.Fuzzer
+	p.Hwaddress = userProps.Hwaddress
+	p.Integer_overflow = userProps.Integer_overflow
+	p.Memtag_heap = userProps.Memtag_heap
+	p.Memtag_stack = userProps.Memtag_stack
+	p.Safestack = userProps.Safestack
+	p.Scs = userProps.Scs
+	p.Scudo = userProps.Scudo
+	p.Thread = userProps.Thread
+	p.Undefined = userProps.Undefined
+	p.Writeonly = userProps.Writeonly
+
+	p.Misc_undefined = make([]string, 0, len(userProps.Misc_undefined))
+	for _, v := range userProps.Misc_undefined {
+		p.Misc_undefined = append(p.Misc_undefined, v)
+	}
+
+	p.Diag.Cfi = userProps.Diag.Cfi
+	p.Diag.Integer_overflow = userProps.Diag.Integer_overflow
+	p.Diag.Memtag_heap = userProps.Diag.Memtag_heap
+	p.Diag.Undefined = userProps.Diag.Undefined
+
+	p.Diag.Misc_undefined = make([]string, 0, len(userProps.Diag.Misc_undefined))
+	for _, v := range userProps.Diag.Misc_undefined {
+		p.Diag.Misc_undefined = append(p.Diag.Misc_undefined, v)
+	}
+}
+
 func (sanitize *sanitize) begin(ctx BaseModuleContext) {
-	s := &sanitize.Properties.Sanitize
+	s := &sanitize.Properties.SanitizeMutated
+	s.copyUserPropertiesToMutated(&sanitize.Properties.Sanitize)
 
 	// Don't apply sanitizers to NDK code.
 	if ctx.useSdk() {
@@ -614,20 +719,13 @@
 	return false
 }
 
-func (sanitize *sanitize) flags(ctx ModuleContext, flags Flags) Flags {
-	if !sanitize.Properties.SanitizerEnabled && !sanitize.Properties.UbsanRuntimeDep {
+func (s *sanitize) flags(ctx ModuleContext, flags Flags) Flags {
+	if !s.Properties.SanitizerEnabled && !s.Properties.UbsanRuntimeDep {
 		return flags
 	}
+	sanProps := &s.Properties.SanitizeMutated
 
-	// Currently unwinding through tagged frames for exceptions is broken, so disable memtag stack
-	// in that case, so we don't end up tagging those.
-	// TODO(b/174878242): Remove once https://r.android.com/2251926 is included in toolchain.
-	if android.InList("-fexceptions", flags.Local.CFlags) || android.InList("-fexceptions", flags.Global.CFlags) {
-		sanitize.Properties.Sanitize.Memtag_stack = nil
-		_, sanitize.Properties.Sanitizers = android.RemoveFromList("memtag-stack", sanitize.Properties.Sanitizers)
-	}
-
-	if Bool(sanitize.Properties.Sanitize.Address) {
+	if Bool(sanProps.Address) {
 		if ctx.Arch().ArchType == android.Arm {
 			// Frame pointer based unwinder in ASan requires ARM frame setup.
 			// TODO: put in flags?
@@ -636,7 +734,7 @@
 		flags.Local.CFlags = append(flags.Local.CFlags, asanCflags...)
 		flags.Local.LdFlags = append(flags.Local.LdFlags, asanLdflags...)
 
-		if Bool(sanitize.Properties.Sanitize.Writeonly) {
+		if Bool(sanProps.Writeonly) {
 			flags.Local.CFlags = append(flags.Local.CFlags, "-mllvm", "-asan-instrument-reads=0")
 		}
 
@@ -657,7 +755,7 @@
 		}
 	}
 
-	if Bool(sanitize.Properties.Sanitize.Hwaddress) {
+	if Bool(sanProps.Hwaddress) {
 		flags.Local.CFlags = append(flags.Local.CFlags, hwasanCflags...)
 
 		for _, flag := range hwasanCommonflags {
@@ -667,12 +765,12 @@
 			flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,-mllvm,"+flag)
 		}
 
-		if Bool(sanitize.Properties.Sanitize.Writeonly) {
+		if Bool(sanProps.Writeonly) {
 			flags.Local.CFlags = append(flags.Local.CFlags, "-mllvm", "-hwasan-instrument-reads=0")
 		}
 	}
 
-	if Bool(sanitize.Properties.Sanitize.Fuzzer) {
+	if Bool(sanProps.Fuzzer) {
 		flags.Local.CFlags = append(flags.Local.CFlags, "-fsanitize=fuzzer-no-link")
 
 		// TODO(b/131771163): LTO and Fuzzer support is mutually incompatible.
@@ -701,7 +799,7 @@
 		flags.Local.LdFlags = append(flags.Local.LdFlags, `-Wl,-rpath,\$$ORIGIN`)
 	}
 
-	if Bool(sanitize.Properties.Sanitize.Cfi) {
+	if Bool(sanProps.Cfi) {
 		if ctx.Arch().ArchType == android.Arm {
 			// __cfi_check needs to be built as Thumb (see the code in linker_cfi.cpp). LLVM is not set up
 			// to do this on a function basis, so force Thumb on the entire module.
@@ -710,7 +808,7 @@
 
 		flags.Local.CFlags = append(flags.Local.CFlags, cfiCflags...)
 		flags.Local.AsFlags = append(flags.Local.AsFlags, cfiAsflags...)
-		if Bool(sanitize.Properties.Sanitize.Config.Cfi_assembly_support) {
+		if Bool(s.Properties.Sanitize.Config.Cfi_assembly_support) {
 			flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize-cfi-canonical-jump-tables")
 		}
 		// Only append the default visibility flag if -fvisibility has not already been set
@@ -726,7 +824,7 @@
 		}
 	}
 
-	if Bool(sanitize.Properties.Sanitize.Memtag_stack) {
+	if Bool(sanProps.Memtag_stack) {
 		flags.Local.CFlags = append(flags.Local.CFlags, memtagStackCommonFlags...)
 		// TODO(fmayer): remove -Wno-error once https://reviews.llvm.org/D127917 is in Android toolchain.
 		flags.Local.CFlags = append(flags.Local.CFlags, "-Wno-error=frame-larger-than")
@@ -737,20 +835,20 @@
 		flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,--no-fatal-warnings")
 	}
 
-	if (Bool(sanitize.Properties.Sanitize.Memtag_heap) || Bool(sanitize.Properties.Sanitize.Memtag_stack)) && ctx.binary() {
-		if Bool(sanitize.Properties.Sanitize.Diag.Memtag_heap) {
+	if (Bool(sanProps.Memtag_heap) || Bool(sanProps.Memtag_stack)) && ctx.binary() {
+		if Bool(sanProps.Diag.Memtag_heap) {
 			flags.Local.LdFlags = append(flags.Local.LdFlags, "-fsanitize-memtag-mode=sync")
 		} else {
 			flags.Local.LdFlags = append(flags.Local.LdFlags, "-fsanitize-memtag-mode=async")
 		}
 	}
 
-	if Bool(sanitize.Properties.Sanitize.Integer_overflow) {
+	if Bool(sanProps.Integer_overflow) {
 		flags.Local.CFlags = append(flags.Local.CFlags, intOverflowCflags...)
 	}
 
-	if len(sanitize.Properties.Sanitizers) > 0 {
-		sanitizeArg := "-fsanitize=" + strings.Join(sanitize.Properties.Sanitizers, ",")
+	if len(s.Properties.Sanitizers) > 0 {
+		sanitizeArg := "-fsanitize=" + strings.Join(s.Properties.Sanitizers, ",")
 		flags.Local.CFlags = append(flags.Local.CFlags, sanitizeArg)
 		flags.Local.AsFlags = append(flags.Local.AsFlags, sanitizeArg)
 		flags.Local.LdFlags = append(flags.Local.LdFlags, sanitizeArg)
@@ -774,7 +872,7 @@
 			flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize=vptr,function")
 		}
 
-		if Bool(sanitize.Properties.Sanitize.Fuzzer) {
+		if Bool(sanProps.Fuzzer) {
 			// When fuzzing, we wish to crash with diagnostics on any bug.
 			flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize-trap=all", "-fno-sanitize-recover=all")
 		} else if ctx.Host() {
@@ -783,7 +881,7 @@
 			flags.Local.CFlags = append(flags.Local.CFlags, "-fsanitize-trap=all", "-ftrap-function=abort")
 		}
 
-		if enableMinimalRuntime(sanitize) {
+		if enableMinimalRuntime(s) {
 			flags.Local.CFlags = append(flags.Local.CFlags, strings.Join(minimalRuntimeFlags, " "))
 		}
 
@@ -797,22 +895,22 @@
 		}
 	}
 
-	if len(sanitize.Properties.DiagSanitizers) > 0 {
-		flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize-trap="+strings.Join(sanitize.Properties.DiagSanitizers, ","))
+	if len(s.Properties.DiagSanitizers) > 0 {
+		flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize-trap="+strings.Join(s.Properties.DiagSanitizers, ","))
 	}
 	// FIXME: enable RTTI if diag + (cfi or vptr)
 
-	if sanitize.Properties.Sanitize.Recover != nil {
+	if s.Properties.Sanitize.Recover != nil {
 		flags.Local.CFlags = append(flags.Local.CFlags, "-fsanitize-recover="+
-			strings.Join(sanitize.Properties.Sanitize.Recover, ","))
+			strings.Join(s.Properties.Sanitize.Recover, ","))
 	}
 
-	if sanitize.Properties.Sanitize.Diag.No_recover != nil {
+	if s.Properties.Sanitize.Diag.No_recover != nil {
 		flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize-recover="+
-			strings.Join(sanitize.Properties.Sanitize.Diag.No_recover, ","))
+			strings.Join(s.Properties.Sanitize.Diag.No_recover, ","))
 	}
 
-	blocklist := android.OptionalPathForModuleSrc(ctx, sanitize.Properties.Sanitize.Blocklist)
+	blocklist := android.OptionalPathForModuleSrc(ctx, s.Properties.Sanitize.Blocklist)
 	if blocklist.Valid() {
 		flags.Local.CFlags = append(flags.Local.CFlags, "-fsanitize-ignorelist="+blocklist.String())
 		flags.CFlagsDeps = append(flags.CFlagsDeps, blocklist.Path())
@@ -821,47 +919,47 @@
 	return flags
 }
 
-func (sanitize *sanitize) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
+func (s *sanitize) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
 	// Add a suffix for cfi/hwasan/scs-enabled static/header libraries to allow surfacing
 	// both the sanitized and non-sanitized variants to make without a name conflict.
 	if entries.Class == "STATIC_LIBRARIES" || entries.Class == "HEADER_LIBRARIES" {
-		if Bool(sanitize.Properties.Sanitize.Cfi) {
+		if Bool(s.Properties.SanitizeMutated.Cfi) {
 			entries.SubName += ".cfi"
 		}
-		if Bool(sanitize.Properties.Sanitize.Hwaddress) {
+		if Bool(s.Properties.SanitizeMutated.Hwaddress) {
 			entries.SubName += ".hwasan"
 		}
-		if Bool(sanitize.Properties.Sanitize.Scs) {
+		if Bool(s.Properties.SanitizeMutated.Scs) {
 			entries.SubName += ".scs"
 		}
 	}
 }
 
-func (sanitize *sanitize) inSanitizerDir() bool {
-	return sanitize.Properties.InSanitizerDir
+func (s *sanitize) inSanitizerDir() bool {
+	return s.Properties.InSanitizerDir
 }
 
 // getSanitizerBoolPtr returns the SanitizerTypes associated bool pointer from SanitizeProperties.
-func (sanitize *sanitize) getSanitizerBoolPtr(t SanitizerType) *bool {
+func (s *sanitize) getSanitizerBoolPtr(t SanitizerType) *bool {
 	switch t {
 	case Asan:
-		return sanitize.Properties.Sanitize.Address
+		return s.Properties.SanitizeMutated.Address
 	case Hwasan:
-		return sanitize.Properties.Sanitize.Hwaddress
+		return s.Properties.SanitizeMutated.Hwaddress
 	case tsan:
-		return sanitize.Properties.Sanitize.Thread
+		return s.Properties.SanitizeMutated.Thread
 	case intOverflow:
-		return sanitize.Properties.Sanitize.Integer_overflow
+		return s.Properties.SanitizeMutated.Integer_overflow
 	case cfi:
-		return sanitize.Properties.Sanitize.Cfi
+		return s.Properties.SanitizeMutated.Cfi
 	case scs:
-		return sanitize.Properties.Sanitize.Scs
+		return s.Properties.SanitizeMutated.Scs
 	case Memtag_heap:
-		return sanitize.Properties.Sanitize.Memtag_heap
+		return s.Properties.SanitizeMutated.Memtag_heap
 	case Memtag_stack:
-		return sanitize.Properties.Sanitize.Memtag_stack
+		return s.Properties.SanitizeMutated.Memtag_stack
 	case Fuzzer:
-		return sanitize.Properties.Sanitize.Fuzzer
+		return s.Properties.SanitizeMutated.Fuzzer
 	default:
 		panic(fmt.Errorf("unknown SanitizerType %d", t))
 	}
@@ -894,28 +992,28 @@
 	}
 	switch t {
 	case Asan:
-		sanitize.Properties.Sanitize.Address = bPtr
+		sanitize.Properties.SanitizeMutated.Address = bPtr
 		// For ASAN variant, we need to disable Memtag_stack
-		sanitize.Properties.Sanitize.Memtag_stack = nil
+		sanitize.Properties.SanitizeMutated.Memtag_stack = nil
 	case Hwasan:
-		sanitize.Properties.Sanitize.Hwaddress = bPtr
+		sanitize.Properties.SanitizeMutated.Hwaddress = bPtr
 		// For HWAsan variant, we need to disable Memtag_stack
-		sanitize.Properties.Sanitize.Memtag_stack = nil
+		sanitize.Properties.SanitizeMutated.Memtag_stack = nil
 	case tsan:
-		sanitize.Properties.Sanitize.Thread = bPtr
+		sanitize.Properties.SanitizeMutated.Thread = bPtr
 	case intOverflow:
-		sanitize.Properties.Sanitize.Integer_overflow = bPtr
+		sanitize.Properties.SanitizeMutated.Integer_overflow = bPtr
 	case cfi:
-		sanitize.Properties.Sanitize.Cfi = bPtr
+		sanitize.Properties.SanitizeMutated.Cfi = bPtr
 	case scs:
-		sanitize.Properties.Sanitize.Scs = bPtr
+		sanitize.Properties.SanitizeMutated.Scs = bPtr
 	case Memtag_heap:
-		sanitize.Properties.Sanitize.Memtag_heap = bPtr
+		sanitize.Properties.SanitizeMutated.Memtag_heap = bPtr
 	case Memtag_stack:
-		sanitize.Properties.Sanitize.Memtag_stack = bPtr
+		sanitize.Properties.SanitizeMutated.Memtag_stack = bPtr
 		// We do not need to disable ASAN or HWASan here, as there is no Memtag_stack variant.
 	case Fuzzer:
-		sanitize.Properties.Sanitize.Fuzzer = bPtr
+		sanitize.Properties.SanitizeMutated.Fuzzer = bPtr
 	default:
 		panic(fmt.Errorf("unknown SanitizerType %d", t))
 	}
@@ -1065,7 +1163,7 @@
 		//TODO: When Rust modules have vendor support, enable this path for PlatformSanitizeable
 
 		// Check if it's a snapshot module supporting sanitizer
-		if ss, ok := c.linker.(snapshotSanitizer); ok && ss.isSanitizerEnabled(s.sanitizer) {
+		if ss, ok := c.linker.(snapshotSanitizer); ok && ss.isSanitizerAvailable(s.sanitizer) {
 			return []string{"", s.sanitizer.variationName()}
 		} else {
 			return []string{""}
@@ -1097,7 +1195,7 @@
 func (s *sanitizerSplitMutator) IncomingTransition(ctx android.IncomingTransitionContext, incomingVariation string) string {
 	if d, ok := ctx.Module().(PlatformSanitizeable); ok {
 		if dm, ok := ctx.Module().(*Module); ok {
-			if ss, ok := dm.linker.(snapshotSanitizer); ok && ss.isSanitizerEnabled(s.sanitizer) {
+			if ss, ok := dm.linker.(snapshotSanitizer); ok && ss.isSanitizerAvailable(s.sanitizer) {
 				return incomingVariation
 			}
 		}
@@ -1127,7 +1225,8 @@
 				return s.sanitizer.variationName()
 			}
 
-			if s.sanitizer == cfi || s.sanitizer == Hwasan || s.sanitizer == scs || s.sanitizer == Asan {
+			// Some sanitizers do not propagate to shared dependencies
+			if !s.sanitizer.shouldPropagateToSharedLibraryDeps() {
 				return ""
 			}
 		}
@@ -1212,14 +1311,23 @@
 			sanitizeable.AddSanitizerDependencies(mctx, s.sanitizer.name())
 		}
 	} else if c, ok := mctx.Module().(*Module); ok {
-		if ss, ok := c.linker.(snapshotSanitizer); ok && ss.isSanitizerEnabled(s.sanitizer) {
+		if ss, ok := c.linker.(snapshotSanitizer); ok && ss.isSanitizerAvailable(s.sanitizer) {
+			if !ss.isUnsanitizedVariant() {
+				// Snapshot sanitizer may have only one variantion.
+				// Skip exporting the module if it already has a sanitizer variation.
+				c.SetPreventInstall()
+				c.SetHideFromMake()
+				return
+			}
 			c.linker.(snapshotSanitizer).setSanitizerVariation(s.sanitizer, sanitizerVariation)
 
 			// Export the static lib name to make
 			if c.static() && c.ExportedToMake() {
+				// use BaseModuleName which is the name for Make.
 				if s.sanitizer == cfi {
-					// use BaseModuleName which is the name for Make.
 					cfiStaticLibs(mctx.Config()).add(c, c.BaseModuleName())
+				} else if s.sanitizer == Hwasan {
+					hwasanStaticLibs(mctx.Config()).add(c, c.BaseModuleName())
 				}
 			}
 		}
@@ -1227,7 +1335,7 @@
 }
 
 func (c *Module) SanitizeNever() bool {
-	return Bool(c.sanitize.Properties.Sanitize.Never)
+	return Bool(c.sanitize.Properties.SanitizeMutated.Never)
 }
 
 func (c *Module) IsSanitizerExplicitlyDisabled(t SanitizerType) bool {
@@ -1295,10 +1403,12 @@
 		var sanitizers []string
 		var diagSanitizers []string
 
-		if Bool(c.sanitize.Properties.Sanitize.All_undefined) {
+		sanProps := &c.sanitize.Properties.SanitizeMutated
+
+		if Bool(sanProps.All_undefined) {
 			sanitizers = append(sanitizers, "undefined")
 		} else {
-			if Bool(c.sanitize.Properties.Sanitize.Undefined) {
+			if Bool(sanProps.Undefined) {
 				sanitizers = append(sanitizers,
 					"bool",
 					"integer-divide-by-zero",
@@ -1323,66 +1433,66 @@
 					// "object-size",
 				)
 			}
-			sanitizers = append(sanitizers, c.sanitize.Properties.Sanitize.Misc_undefined...)
+			sanitizers = append(sanitizers, sanProps.Misc_undefined...)
 		}
 
-		if Bool(c.sanitize.Properties.Sanitize.Diag.Undefined) {
+		if Bool(sanProps.Diag.Undefined) {
 			diagSanitizers = append(diagSanitizers, "undefined")
 		}
 
-		diagSanitizers = append(diagSanitizers, c.sanitize.Properties.Sanitize.Diag.Misc_undefined...)
+		diagSanitizers = append(diagSanitizers, sanProps.Diag.Misc_undefined...)
 
-		if Bool(c.sanitize.Properties.Sanitize.Address) {
+		if Bool(sanProps.Address) {
 			sanitizers = append(sanitizers, "address")
 			diagSanitizers = append(diagSanitizers, "address")
 		}
 
-		if Bool(c.sanitize.Properties.Sanitize.Hwaddress) {
+		if Bool(sanProps.Hwaddress) {
 			sanitizers = append(sanitizers, "hwaddress")
 		}
 
-		if Bool(c.sanitize.Properties.Sanitize.Thread) {
+		if Bool(sanProps.Thread) {
 			sanitizers = append(sanitizers, "thread")
 		}
 
-		if Bool(c.sanitize.Properties.Sanitize.Safestack) {
+		if Bool(sanProps.Safestack) {
 			sanitizers = append(sanitizers, "safe-stack")
 		}
 
-		if Bool(c.sanitize.Properties.Sanitize.Cfi) {
+		if Bool(sanProps.Cfi) {
 			sanitizers = append(sanitizers, "cfi")
 
-			if Bool(c.sanitize.Properties.Sanitize.Diag.Cfi) {
+			if Bool(sanProps.Diag.Cfi) {
 				diagSanitizers = append(diagSanitizers, "cfi")
 			}
 		}
 
-		if Bool(c.sanitize.Properties.Sanitize.Integer_overflow) {
+		if Bool(sanProps.Integer_overflow) {
 			sanitizers = append(sanitizers, "unsigned-integer-overflow")
 			sanitizers = append(sanitizers, "signed-integer-overflow")
-			if Bool(c.sanitize.Properties.Sanitize.Diag.Integer_overflow) {
+			if Bool(sanProps.Diag.Integer_overflow) {
 				diagSanitizers = append(diagSanitizers, "unsigned-integer-overflow")
 				diagSanitizers = append(diagSanitizers, "signed-integer-overflow")
 			}
 		}
 
-		if Bool(c.sanitize.Properties.Sanitize.Scudo) {
+		if Bool(sanProps.Scudo) {
 			sanitizers = append(sanitizers, "scudo")
 		}
 
-		if Bool(c.sanitize.Properties.Sanitize.Scs) {
+		if Bool(sanProps.Scs) {
 			sanitizers = append(sanitizers, "shadow-call-stack")
 		}
 
-		if Bool(c.sanitize.Properties.Sanitize.Memtag_heap) && c.Binary() {
+		if Bool(sanProps.Memtag_heap) && c.Binary() {
 			sanitizers = append(sanitizers, "memtag-heap")
 		}
 
-		if Bool(c.sanitize.Properties.Sanitize.Memtag_stack) {
+		if Bool(sanProps.Memtag_stack) {
 			sanitizers = append(sanitizers, "memtag-stack")
 		}
 
-		if Bool(c.sanitize.Properties.Sanitize.Fuzzer) {
+		if Bool(sanProps.Fuzzer) {
 			sanitizers = append(sanitizers, "fuzzer-no-link")
 		}
 
@@ -1401,27 +1511,27 @@
 		alwaysStaticRuntime := false
 		var extraStaticDeps []string
 		toolchain := c.toolchain(mctx)
-		if Bool(c.sanitize.Properties.Sanitize.Address) {
+		if Bool(sanProps.Address) {
 			runtimeLibrary = config.AddressSanitizerRuntimeLibrary(toolchain)
-		} else if Bool(c.sanitize.Properties.Sanitize.Hwaddress) {
+		} else if Bool(sanProps.Hwaddress) {
 			if c.staticBinary() {
 				runtimeLibrary = config.HWAddressSanitizerStaticLibrary(toolchain)
 				extraStaticDeps = []string{"libdl"}
 			} else {
 				runtimeLibrary = config.HWAddressSanitizerRuntimeLibrary(toolchain)
 			}
-		} else if Bool(c.sanitize.Properties.Sanitize.Thread) {
+		} else if Bool(sanProps.Thread) {
 			runtimeLibrary = config.ThreadSanitizerRuntimeLibrary(toolchain)
-		} else if Bool(c.sanitize.Properties.Sanitize.Scudo) {
+		} else if Bool(sanProps.Scudo) {
 			if len(diagSanitizers) == 0 && !c.sanitize.Properties.UbsanRuntimeDep {
 				runtimeLibrary = config.ScudoMinimalRuntimeLibrary(toolchain)
 			} else {
 				runtimeLibrary = config.ScudoRuntimeLibrary(toolchain)
 			}
 		} else if len(diagSanitizers) > 0 || c.sanitize.Properties.UbsanRuntimeDep ||
-			Bool(c.sanitize.Properties.Sanitize.Fuzzer) ||
-			Bool(c.sanitize.Properties.Sanitize.Undefined) ||
-			Bool(c.sanitize.Properties.Sanitize.All_undefined) {
+			Bool(sanProps.Fuzzer) ||
+			Bool(sanProps.Undefined) ||
+			Bool(sanProps.All_undefined) {
 			runtimeLibrary = config.UndefinedBehaviorSanitizerRuntimeLibrary(toolchain)
 			if c.staticBinary() || toolchain.Musl() {
 				// Use a static runtime for static binaries.
@@ -1632,21 +1742,27 @@
 }
 
 func enableMinimalRuntime(sanitize *sanitize) bool {
-	if !Bool(sanitize.Properties.Sanitize.Address) &&
-		!Bool(sanitize.Properties.Sanitize.Hwaddress) &&
-		!Bool(sanitize.Properties.Sanitize.Fuzzer) &&
-		(Bool(sanitize.Properties.Sanitize.Integer_overflow) ||
-			len(sanitize.Properties.Sanitize.Misc_undefined) > 0 ||
-			Bool(sanitize.Properties.Sanitize.Undefined) ||
-			Bool(sanitize.Properties.Sanitize.All_undefined)) &&
-		!(Bool(sanitize.Properties.Sanitize.Diag.Integer_overflow) ||
-			Bool(sanitize.Properties.Sanitize.Diag.Cfi) ||
-			Bool(sanitize.Properties.Sanitize.Diag.Undefined) ||
-			len(sanitize.Properties.Sanitize.Diag.Misc_undefined) > 0) {
-
-		return true
+	if sanitize.isSanitizerEnabled(Asan) {
+		return false
+	} else if sanitize.isSanitizerEnabled(Hwasan) {
+		return false
+	} else if sanitize.isSanitizerEnabled(Fuzzer) {
+		return false
 	}
-	return false
+
+	if enableUbsanRuntime(sanitize) {
+		return false
+	}
+
+	sanitizeProps := &sanitize.Properties.SanitizeMutated
+	if Bool(sanitizeProps.Diag.Cfi) {
+		return false
+	}
+
+	return Bool(sanitizeProps.Integer_overflow) ||
+		len(sanitizeProps.Misc_undefined) > 0 ||
+		Bool(sanitizeProps.Undefined) ||
+		Bool(sanitizeProps.All_undefined)
 }
 
 func (m *Module) UbsanRuntimeNeeded() bool {
@@ -1658,9 +1774,10 @@
 }
 
 func enableUbsanRuntime(sanitize *sanitize) bool {
-	return Bool(sanitize.Properties.Sanitize.Diag.Integer_overflow) ||
-		Bool(sanitize.Properties.Sanitize.Diag.Undefined) ||
-		len(sanitize.Properties.Sanitize.Diag.Misc_undefined) > 0
+	sanitizeProps := &sanitize.Properties.SanitizeMutated
+	return Bool(sanitizeProps.Diag.Integer_overflow) ||
+		Bool(sanitizeProps.Diag.Undefined) ||
+		len(sanitizeProps.Diag.Misc_undefined) > 0
 }
 
 func cfiMakeVarsProvider(ctx android.MakeVarsContext) {
diff --git a/cc/sanitize_test.go b/cc/sanitize_test.go
index 2393f3e..b102d33 100644
--- a/cc/sanitize_test.go
+++ b/cc/sanitize_test.go
@@ -21,6 +21,8 @@
 	"testing"
 
 	"android/soong/android"
+
+	"github.com/google/blueprint"
 )
 
 var prepareForAsanTest = android.FixtureAddFile("asan/Android.bp", []byte(`
@@ -29,6 +31,60 @@
 	}
 `))
 
+var prepareForTsanTest = android.FixtureAddFile("tsan/Android.bp", []byte(`
+	cc_library_shared {
+		name: "libclang_rt.tsan",
+	}
+`))
+
+type providerInterface interface {
+	ModuleProvider(blueprint.Module, blueprint.ProviderKey) interface{}
+}
+
+// expectSharedLinkDep verifies that the from module links against the to module as a
+// shared library.
+func expectSharedLinkDep(t *testing.T, ctx providerInterface, from, to android.TestingModule) {
+	t.Helper()
+	fromLink := from.Description("link")
+	toInfo := ctx.ModuleProvider(to.Module(), SharedLibraryInfoProvider).(SharedLibraryInfo)
+
+	if g, w := fromLink.OrderOnly.Strings(), toInfo.SharedLibrary.RelativeToTop().String(); !android.InList(w, g) {
+		t.Errorf("%s should link against %s, expected %q, got %q",
+			from.Module(), to.Module(), w, g)
+	}
+}
+
+// expectStaticLinkDep verifies that the from module links against the to module as a
+// static library.
+func expectStaticLinkDep(t *testing.T, ctx providerInterface, from, to android.TestingModule) {
+	t.Helper()
+	fromLink := from.Description("link")
+	toInfo := ctx.ModuleProvider(to.Module(), StaticLibraryInfoProvider).(StaticLibraryInfo)
+
+	if g, w := fromLink.Implicits.Strings(), toInfo.StaticLibrary.RelativeToTop().String(); !android.InList(w, g) {
+		t.Errorf("%s should link against %s, expected %q, got %q",
+			from.Module(), to.Module(), w, g)
+	}
+
+}
+
+// expectInstallDep verifies that the install rule of the from module depends on the
+// install rule of the to module.
+func expectInstallDep(t *testing.T, from, to android.TestingModule) {
+	t.Helper()
+	fromInstalled := from.Description("install")
+	toInstalled := to.Description("install")
+
+	// combine implicits and order-only dependencies, host uses implicit but device uses
+	// order-only.
+	got := append(fromInstalled.Implicits.Strings(), fromInstalled.OrderOnly.Strings()...)
+	want := toInstalled.Output.String()
+	if !android.InList(want, got) {
+		t.Errorf("%s installation should depend on %s, expected %q, got %q",
+			from.Module(), to.Module(), want, got)
+	}
+}
+
 func TestAsan(t *testing.T) {
 	bp := `
 		cc_binary {
@@ -111,6 +167,7 @@
 	).RunTestWithBp(t, bp)
 
 	check := func(t *testing.T, result *android.TestResult, variant string) {
+		ctx := result.TestContext
 		asanVariant := variant + "_asan"
 		sharedVariant := variant + "_shared"
 		sharedAsanVariant := sharedVariant + "_asan"
@@ -140,85 +197,359 @@
 		libStaticAsan := result.ModuleForTests("libstatic_asan", staticAsanVariant)
 		libStaticAsanNoAsanVariant := result.ModuleForTests("libstatic_asan", staticVariant)
 
-		// expectSharedLinkDep verifies that the from module links against the to module as a
-		// shared library.
-		expectSharedLinkDep := func(from, to android.TestingModule) {
-			t.Helper()
-			fromLink := from.Description("link")
-			toLink := to.Description("strip")
+		expectSharedLinkDep(t, ctx, binWithAsan, libShared)
+		expectSharedLinkDep(t, ctx, binWithAsan, libAsan)
+		expectSharedLinkDep(t, ctx, libShared, libTransitive)
+		expectSharedLinkDep(t, ctx, libAsan, libTransitive)
 
-			if g, w := fromLink.OrderOnly.Strings(), toLink.Output.String(); !android.InList(w, g) {
-				t.Errorf("%s should link against %s, expected %q, got %q",
-					from.Module(), to.Module(), w, g)
-			}
-		}
+		expectStaticLinkDep(t, ctx, binWithAsan, libStaticAsanVariant)
+		expectStaticLinkDep(t, ctx, binWithAsan, libNoAsan)
+		expectStaticLinkDep(t, ctx, binWithAsan, libStaticAsan)
 
-		// expectStaticLinkDep verifies that the from module links against the to module as a
-		// static library.
-		expectStaticLinkDep := func(from, to android.TestingModule) {
-			t.Helper()
-			fromLink := from.Description("link")
-			toLink := to.Description("static link")
+		expectInstallDep(t, binWithAsan, libShared)
+		expectInstallDep(t, binWithAsan, libAsan)
+		expectInstallDep(t, binWithAsan, libTransitive)
+		expectInstallDep(t, libShared, libTransitive)
+		expectInstallDep(t, libAsan, libTransitive)
 
-			if g, w := fromLink.Implicits.Strings(), toLink.Output.String(); !android.InList(w, g) {
-				t.Errorf("%s should link against %s, expected %q, got %q",
-					from.Module(), to.Module(), w, g)
-			}
+		expectSharedLinkDep(t, ctx, binNoAsan, libShared)
+		expectSharedLinkDep(t, ctx, binNoAsan, libAsan)
+		expectSharedLinkDep(t, ctx, libShared, libTransitive)
+		expectSharedLinkDep(t, ctx, libAsan, libTransitive)
 
-		}
+		expectStaticLinkDep(t, ctx, binNoAsan, libStaticNoAsanVariant)
+		expectStaticLinkDep(t, ctx, binNoAsan, libNoAsan)
+		expectStaticLinkDep(t, ctx, binNoAsan, libStaticAsanNoAsanVariant)
 
-		// expectInstallDep verifies that the install rule of the from module depends on the
-		// install rule of the to module.
-		expectInstallDep := func(from, to android.TestingModule) {
-			t.Helper()
-			fromInstalled := from.Description("install")
-			toInstalled := to.Description("install")
-
-			// combine implicits and order-only dependencies, host uses implicit but device uses
-			// order-only.
-			got := append(fromInstalled.Implicits.Strings(), fromInstalled.OrderOnly.Strings()...)
-			want := toInstalled.Output.String()
-			if !android.InList(want, got) {
-				t.Errorf("%s installation should depend on %s, expected %q, got %q",
-					from.Module(), to.Module(), want, got)
-			}
-		}
-
-		expectSharedLinkDep(binWithAsan, libShared)
-		expectSharedLinkDep(binWithAsan, libAsan)
-		expectSharedLinkDep(libShared, libTransitive)
-		expectSharedLinkDep(libAsan, libTransitive)
-
-		expectStaticLinkDep(binWithAsan, libStaticAsanVariant)
-		expectStaticLinkDep(binWithAsan, libNoAsan)
-		expectStaticLinkDep(binWithAsan, libStaticAsan)
-
-		expectInstallDep(binWithAsan, libShared)
-		expectInstallDep(binWithAsan, libAsan)
-		expectInstallDep(binWithAsan, libTransitive)
-		expectInstallDep(libShared, libTransitive)
-		expectInstallDep(libAsan, libTransitive)
-
-		expectSharedLinkDep(binNoAsan, libShared)
-		expectSharedLinkDep(binNoAsan, libAsan)
-		expectSharedLinkDep(libShared, libTransitive)
-		expectSharedLinkDep(libAsan, libTransitive)
-
-		expectStaticLinkDep(binNoAsan, libStaticNoAsanVariant)
-		expectStaticLinkDep(binNoAsan, libNoAsan)
-		expectStaticLinkDep(binNoAsan, libStaticAsanNoAsanVariant)
-
-		expectInstallDep(binNoAsan, libShared)
-		expectInstallDep(binNoAsan, libAsan)
-		expectInstallDep(binNoAsan, libTransitive)
-		expectInstallDep(libShared, libTransitive)
-		expectInstallDep(libAsan, libTransitive)
+		expectInstallDep(t, binNoAsan, libShared)
+		expectInstallDep(t, binNoAsan, libAsan)
+		expectInstallDep(t, binNoAsan, libTransitive)
+		expectInstallDep(t, libShared, libTransitive)
+		expectInstallDep(t, libAsan, libTransitive)
 	}
 
 	t.Run("host", func(t *testing.T) { check(t, result, result.Config.BuildOSTarget.String()) })
 	t.Run("device", func(t *testing.T) { check(t, result, "android_arm64_armv8-a") })
 }
 
+func TestTsan(t *testing.T) {
+	bp := `
+	cc_binary {
+		name: "bin_with_tsan",
+		host_supported: true,
+		shared_libs: [
+			"libshared",
+			"libtsan",
+		],
+		sanitize: {
+			thread: true,
+		}
+	}
+
+	cc_binary {
+		name: "bin_no_tsan",
+		host_supported: true,
+		shared_libs: [
+			"libshared",
+			"libtsan",
+		],
+	}
+
+	cc_library_shared {
+		name: "libshared",
+		host_supported: true,
+		shared_libs: ["libtransitive"],
+	}
+
+	cc_library_shared {
+		name: "libtsan",
+		host_supported: true,
+		shared_libs: ["libtransitive"],
+		sanitize: {
+			thread: true,
+		}
+	}
+
+	cc_library_shared {
+		name: "libtransitive",
+		host_supported: true,
+	}
+`
+
+	result := android.GroupFixturePreparers(
+		prepareForCcTest,
+		prepareForTsanTest,
+	).RunTestWithBp(t, bp)
+
+	check := func(t *testing.T, result *android.TestResult, variant string) {
+		ctx := result.TestContext
+		tsanVariant := variant + "_tsan"
+		sharedVariant := variant + "_shared"
+		sharedTsanVariant := sharedVariant + "_tsan"
+
+		// The binaries, one with tsan and one without
+		binWithTsan := result.ModuleForTests("bin_with_tsan", tsanVariant)
+		binNoTsan := result.ModuleForTests("bin_no_tsan", variant)
+
+		// Shared libraries that don't request tsan
+		libShared := result.ModuleForTests("libshared", sharedVariant)
+		libTransitive := result.ModuleForTests("libtransitive", sharedVariant)
+
+		// Shared library that requests tsan
+		libTsan := result.ModuleForTests("libtsan", sharedTsanVariant)
+
+		expectSharedLinkDep(t, ctx, binWithTsan, libShared)
+		expectSharedLinkDep(t, ctx, binWithTsan, libTsan)
+		expectSharedLinkDep(t, ctx, libShared, libTransitive)
+		expectSharedLinkDep(t, ctx, libTsan, libTransitive)
+
+		expectSharedLinkDep(t, ctx, binNoTsan, libShared)
+		expectSharedLinkDep(t, ctx, binNoTsan, libTsan)
+		expectSharedLinkDep(t, ctx, libShared, libTransitive)
+		expectSharedLinkDep(t, ctx, libTsan, libTransitive)
+	}
+
+	t.Run("host", func(t *testing.T) { check(t, result, result.Config.BuildOSTarget.String()) })
+	t.Run("device", func(t *testing.T) { check(t, result, "android_arm64_armv8-a") })
+}
+
+func TestMiscUndefined(t *testing.T) {
+	if runtime.GOOS != "linux" {
+		t.Skip("requires linux")
+	}
+
+	bp := `
+	cc_binary {
+		name: "bin_with_ubsan",
+		srcs: ["src.cc"],
+		host_supported: true,
+		static_libs: [
+			"libstatic",
+			"libubsan",
+		],
+		sanitize: {
+			misc_undefined: ["integer"],
+		}
+	}
+
+	cc_binary {
+		name: "bin_no_ubsan",
+		host_supported: true,
+		srcs: ["src.cc"],
+		static_libs: [
+			"libstatic",
+			"libubsan",
+		],
+	}
+
+	cc_library_static {
+		name: "libstatic",
+		host_supported: true,
+		srcs: ["src.cc"],
+		static_libs: ["libtransitive"],
+	}
+
+	cc_library_static {
+		name: "libubsan",
+		host_supported: true,
+		srcs: ["src.cc"],
+		whole_static_libs: ["libtransitive"],
+		sanitize: {
+			misc_undefined: ["integer"],
+		}
+	}
+
+	cc_library_static {
+		name: "libtransitive",
+		host_supported: true,
+		srcs: ["src.cc"],
+	}
+`
+
+	result := android.GroupFixturePreparers(
+		prepareForCcTest,
+	).RunTestWithBp(t, bp)
+
+	check := func(t *testing.T, result *android.TestResult, variant string) {
+		ctx := result.TestContext
+		staticVariant := variant + "_static"
+
+		// The binaries, one with ubsan and one without
+		binWithUbsan := result.ModuleForTests("bin_with_ubsan", variant)
+		binNoUbsan := result.ModuleForTests("bin_no_ubsan", variant)
+
+		// Static libraries that don't request ubsan
+		libStatic := result.ModuleForTests("libstatic", staticVariant)
+		libTransitive := result.ModuleForTests("libtransitive", staticVariant)
+
+		libUbsan := result.ModuleForTests("libubsan", staticVariant)
+
+		libUbsanMinimal := result.ModuleForTests("libclang_rt.ubsan_minimal", staticVariant)
+
+		expectStaticLinkDep(t, ctx, binWithUbsan, libStatic)
+		expectStaticLinkDep(t, ctx, binWithUbsan, libUbsan)
+		expectStaticLinkDep(t, ctx, binWithUbsan, libUbsanMinimal)
+
+		miscUndefinedSanFlag := "-fsanitize=integer"
+		binWithUbsanCflags := binWithUbsan.Rule("cc").Args["cFlags"]
+		if !strings.Contains(binWithUbsanCflags, miscUndefinedSanFlag) {
+			t.Errorf("'bin_with_ubsan' Expected %q to be in flags %q, was not", miscUndefinedSanFlag, binWithUbsanCflags)
+		}
+		libStaticCflags := libStatic.Rule("cc").Args["cFlags"]
+		if strings.Contains(libStaticCflags, miscUndefinedSanFlag) {
+			t.Errorf("'libstatic' Expected %q to NOT be in flags %q, was", miscUndefinedSanFlag, binWithUbsanCflags)
+		}
+		libUbsanCflags := libUbsan.Rule("cc").Args["cFlags"]
+		if !strings.Contains(libUbsanCflags, miscUndefinedSanFlag) {
+			t.Errorf("'libubsan' Expected %q to be in flags %q, was not", miscUndefinedSanFlag, binWithUbsanCflags)
+		}
+		libTransitiveCflags := libTransitive.Rule("cc").Args["cFlags"]
+		if strings.Contains(libTransitiveCflags, miscUndefinedSanFlag) {
+			t.Errorf("'libtransitive': Expected %q to NOT be in flags %q, was", miscUndefinedSanFlag, binWithUbsanCflags)
+		}
+
+		expectStaticLinkDep(t, ctx, binNoUbsan, libStatic)
+		expectStaticLinkDep(t, ctx, binNoUbsan, libUbsan)
+	}
+
+	t.Run("host", func(t *testing.T) { check(t, result, result.Config.BuildOSTarget.String()) })
+	t.Run("device", func(t *testing.T) { check(t, result, "android_arm64_armv8-a") })
+}
+
+func TestFuzz(t *testing.T) {
+	bp := `
+		cc_binary {
+			name: "bin_with_fuzzer",
+			host_supported: true,
+			shared_libs: [
+				"libshared",
+				"libfuzzer",
+			],
+			static_libs: [
+				"libstatic",
+				"libnofuzzer",
+				"libstatic_fuzzer",
+			],
+			sanitize: {
+				fuzzer: true,
+			}
+		}
+
+		cc_binary {
+			name: "bin_no_fuzzer",
+			host_supported: true,
+			shared_libs: [
+				"libshared",
+				"libfuzzer",
+			],
+			static_libs: [
+				"libstatic",
+				"libnofuzzer",
+				"libstatic_fuzzer",
+			],
+		}
+
+		cc_library_shared {
+			name: "libshared",
+			host_supported: true,
+			shared_libs: ["libtransitive"],
+		}
+
+		cc_library_shared {
+			name: "libfuzzer",
+			host_supported: true,
+			shared_libs: ["libtransitive"],
+			sanitize: {
+				fuzzer: true,
+			}
+		}
+
+		cc_library_shared {
+			name: "libtransitive",
+			host_supported: true,
+		}
+
+		cc_library_static {
+			name: "libstatic",
+			host_supported: true,
+		}
+
+		cc_library_static {
+			name: "libnofuzzer",
+			host_supported: true,
+			sanitize: {
+				fuzzer: false,
+			}
+		}
+
+		cc_library_static {
+			name: "libstatic_fuzzer",
+			host_supported: true,
+		}
+
+	`
+
+	result := android.GroupFixturePreparers(
+		prepareForCcTest,
+	).RunTestWithBp(t, bp)
+
+	check := func(t *testing.T, result *android.TestResult, variant string) {
+		ctx := result.TestContext
+		fuzzerVariant := variant + "_fuzzer"
+		sharedVariant := variant + "_shared"
+		sharedFuzzerVariant := sharedVariant + "_fuzzer"
+		staticVariant := variant + "_static"
+		staticFuzzerVariant := staticVariant + "_fuzzer"
+
+		// The binaries, one with fuzzer and one without
+		binWithFuzzer := result.ModuleForTests("bin_with_fuzzer", fuzzerVariant)
+		binNoFuzzer := result.ModuleForTests("bin_no_fuzzer", variant)
+
+		// Shared libraries that don't request fuzzer
+		libShared := result.ModuleForTests("libshared", sharedVariant)
+		libTransitive := result.ModuleForTests("libtransitive", sharedVariant)
+
+		// Shared libraries that don't request fuzzer
+		libSharedFuzzer := result.ModuleForTests("libshared", sharedFuzzerVariant)
+		libTransitiveFuzzer := result.ModuleForTests("libtransitive", sharedFuzzerVariant)
+
+		// Shared library that requests fuzzer
+		libFuzzer := result.ModuleForTests("libfuzzer", sharedFuzzerVariant)
+
+		// Static library that uses an fuzzer variant for bin_with_fuzzer and a non-fuzzer variant
+		// for bin_no_fuzzer.
+		libStaticFuzzerVariant := result.ModuleForTests("libstatic", staticFuzzerVariant)
+		libStaticNoFuzzerVariant := result.ModuleForTests("libstatic", staticVariant)
+
+		// Static library that never uses fuzzer.
+		libNoFuzzer := result.ModuleForTests("libnofuzzer", staticVariant)
+
+		// Static library that specifies fuzzer
+		libStaticFuzzer := result.ModuleForTests("libstatic_fuzzer", staticFuzzerVariant)
+		libStaticFuzzerNoFuzzerVariant := result.ModuleForTests("libstatic_fuzzer", staticVariant)
+
+		expectSharedLinkDep(t, ctx, binWithFuzzer, libSharedFuzzer)
+		expectSharedLinkDep(t, ctx, binWithFuzzer, libFuzzer)
+		expectSharedLinkDep(t, ctx, libSharedFuzzer, libTransitiveFuzzer)
+		expectSharedLinkDep(t, ctx, libFuzzer, libTransitiveFuzzer)
+
+		expectStaticLinkDep(t, ctx, binWithFuzzer, libStaticFuzzerVariant)
+		expectStaticLinkDep(t, ctx, binWithFuzzer, libNoFuzzer)
+		expectStaticLinkDep(t, ctx, binWithFuzzer, libStaticFuzzer)
+
+		expectSharedLinkDep(t, ctx, binNoFuzzer, libShared)
+		expectSharedLinkDep(t, ctx, binNoFuzzer, libFuzzer)
+		expectSharedLinkDep(t, ctx, libShared, libTransitive)
+		expectSharedLinkDep(t, ctx, libFuzzer, libTransitiveFuzzer)
+
+		expectStaticLinkDep(t, ctx, binNoFuzzer, libStaticNoFuzzerVariant)
+		expectStaticLinkDep(t, ctx, binNoFuzzer, libNoFuzzer)
+		expectStaticLinkDep(t, ctx, binNoFuzzer, libStaticFuzzerNoFuzzerVariant)
+	}
+
+	t.Run("device", func(t *testing.T) { check(t, result, "android_arm64_armv8-a") })
+}
+
 func TestUbsan(t *testing.T) {
 	if runtime.GOOS != "linux" {
 		t.Skip("requires linux")
diff --git a/cc/sdk.go b/cc/sdk.go
index a0d196b..3e50c9f 100644
--- a/cc/sdk.go
+++ b/cc/sdk.go
@@ -31,6 +31,7 @@
 
 	switch m := ctx.Module().(type) {
 	case LinkableInterface:
+		ccModule, isCcModule := ctx.Module().(*Module)
 		if m.AlwaysSdk() {
 			if !m.UseSdk() && !m.SplitPerApiLevel() {
 				ctx.ModuleErrorf("UseSdk() must return true when AlwaysSdk is set, did the factory forget to set Sdk_version?")
@@ -58,11 +59,32 @@
 				modules[1].(*Module).Properties.PreventInstall = true
 			}
 			ctx.AliasVariation("")
+		} else if isCcModule && ccModule.isImportedApiLibrary() {
+			apiLibrary, _ := ccModule.linker.(*apiLibraryDecorator)
+			if apiLibrary.hasNDKStubs() && ccModule.canUseSdk() {
+				// Handle cc_api_library module with NDK stubs and variants only which can use SDK
+				modules := ctx.CreateVariations("", "sdk")
+				modules[1].(*Module).Properties.IsSdkVariant = true
+				if ctx.Config().UnbundledBuildApps() {
+					// For an unbundled apps build, hide the platform variant from Make.
+					modules[0].(*Module).Properties.HideFromMake = true
+					modules[0].(*Module).Properties.PreventInstall = true
+				} else {
+					// For a platform build, mark the SDK variant so that it gets a ".sdk" suffix when
+					// exposed to Make.
+					modules[1].(*Module).Properties.SdkAndPlatformVariantVisibleToMake = true
+					modules[1].(*Module).Properties.PreventInstall = true
+				}
+			} else {
+				ccModule.Properties.Sdk_version = nil
+				ctx.CreateVariations("")
+				ctx.AliasVariation("")
+			}
 		} else {
-			if m, ok := ctx.Module().(*Module); ok {
+			if isCcModule {
 				// Clear the sdk_version property for modules that don't have an SDK variant so
 				// later code doesn't get confused by it.
-				m.Properties.Sdk_version = nil
+				ccModule.Properties.Sdk_version = nil
 			}
 			ctx.CreateVariations("")
 			ctx.AliasVariation("")
@@ -79,6 +101,11 @@
 	case *snapshotModule:
 		ctx.CreateVariations("")
 	case *CcApiVariant:
-		ctx.CreateVariations("")
+		ccApiVariant, _ := ctx.Module().(*CcApiVariant)
+		if String(ccApiVariant.properties.Variant) == "ndk" {
+			ctx.CreateVariations("sdk")
+		} else {
+			ctx.CreateVariations("")
+		}
 	}
 }
diff --git a/cc/snapshot_prebuilt.go b/cc/snapshot_prebuilt.go
index 792ffe3..570300b 100644
--- a/cc/snapshot_prebuilt.go
+++ b/cc/snapshot_prebuilt.go
@@ -18,6 +18,7 @@
 // snapshot mutators and snapshot information maps which are also defined in this file.
 
 import (
+	"fmt"
 	"strings"
 
 	"android/soong/android"
@@ -399,8 +400,10 @@
 }
 
 type snapshotSanitizer interface {
-	isSanitizerEnabled(t SanitizerType) bool
+	isSanitizerAvailable(t SanitizerType) bool
 	setSanitizerVariation(t SanitizerType, enabled bool)
+	isSanitizerEnabled(t SanitizerType) bool
+	isUnsanitizedVariant() bool
 }
 
 type snapshotLibraryDecorator struct {
@@ -408,10 +411,13 @@
 	*libraryDecorator
 	properties          SnapshotLibraryProperties
 	sanitizerProperties struct {
-		CfiEnabled bool `blueprint:"mutated"`
+		SanitizerVariation SanitizerType `blueprint:"mutated"`
 
 		// Library flags for cfi variant.
 		Cfi SnapshotLibraryProperties `android:"arch_variant"`
+
+		// Library flags for hwasan variant.
+		Hwasan SnapshotLibraryProperties `android:"arch_variant"`
 	}
 }
 
@@ -450,8 +456,10 @@
 		return p.libraryDecorator.link(ctx, flags, deps, objs)
 	}
 
-	if p.sanitizerProperties.CfiEnabled {
+	if p.isSanitizerEnabled(cfi) {
 		p.properties = p.sanitizerProperties.Cfi
+	} else if p.isSanitizerEnabled(Hwasan) {
+		p.properties = p.sanitizerProperties.Hwasan
 	}
 
 	if !p.MatchesWithDevice(ctx.DeviceConfig()) {
@@ -514,25 +522,34 @@
 	return false
 }
 
-func (p *snapshotLibraryDecorator) isSanitizerEnabled(t SanitizerType) bool {
+func (p *snapshotLibraryDecorator) isSanitizerAvailable(t SanitizerType) bool {
 	switch t {
 	case cfi:
 		return p.sanitizerProperties.Cfi.Src != nil
+	case Hwasan:
+		return p.sanitizerProperties.Hwasan.Src != nil
 	default:
 		return false
 	}
 }
 
 func (p *snapshotLibraryDecorator) setSanitizerVariation(t SanitizerType, enabled bool) {
-	if !enabled {
+	if !enabled || p.isSanitizerEnabled(t) {
 		return
 	}
-	switch t {
-	case cfi:
-		p.sanitizerProperties.CfiEnabled = true
-	default:
-		return
+	if !p.isUnsanitizedVariant() {
+		panic(fmt.Errorf("snapshot Sanitizer must be one of Cfi or Hwasan but not both"))
 	}
+	p.sanitizerProperties.SanitizerVariation = t
+}
+
+func (p *snapshotLibraryDecorator) isSanitizerEnabled(t SanitizerType) bool {
+	return p.sanitizerProperties.SanitizerVariation == t
+}
+
+func (p *snapshotLibraryDecorator) isUnsanitizedVariant() bool {
+	return !p.isSanitizerEnabled(Asan) &&
+		!p.isSanitizerEnabled(Hwasan)
 }
 
 func snapshotLibraryFactory(image SnapshotImage, moduleSuffix string) (*Module, *snapshotLibraryDecorator) {
diff --git a/cc/vendor_snapshot_test.go b/cc/vendor_snapshot_test.go
index 6a98778..79405e9 100644
--- a/cc/vendor_snapshot_test.go
+++ b/cc/vendor_snapshot_test.go
@@ -1053,6 +1053,7 @@
 			},
 		},
 	}
+
 	vendor_snapshot_static {
 		name: "libsnapshot",
 		vendor: true,
@@ -1063,7 +1064,10 @@
 				src: "libsnapshot.a",
 				cfi: {
 					src: "libsnapshot.cfi.a",
-				}
+				},
+				hwasan: {
+					src: "libsnapshot.hwasan.a",
+				},
 			},
 		},
 	}
@@ -1098,6 +1102,7 @@
 		"vendor/libc++demangle.a":        nil,
 		"vendor/libsnapshot.a":           nil,
 		"vendor/libsnapshot.cfi.a":       nil,
+		"vendor/libsnapshot.hwasan.a":    nil,
 		"vendor/note_memtag_heap_sync.a": nil,
 	}
 
@@ -1106,15 +1111,25 @@
 	config.TestProductVariables.Platform_vndk_version = StringPtr("29")
 	ctx := testCcWithConfig(t, config)
 
-	// Check non-cfi and cfi variant.
+	// Check non-cfi, cfi and hwasan variant.
 	staticVariant := "android_vendor.28_arm64_armv8-a_static"
 	staticCfiVariant := "android_vendor.28_arm64_armv8-a_static_cfi"
+	staticHwasanVariant := "android_vendor.28_arm64_armv8-a_static_hwasan"
+	staticHwasanCfiVariant := "android_vendor.28_arm64_armv8-a_static_hwasan_cfi"
 
 	staticModule := ctx.ModuleForTests("libsnapshot.vendor_static.28.arm64", staticVariant).Module().(*Module)
 	assertString(t, staticModule.outputFile.Path().Base(), "libsnapshot.a")
 
 	staticCfiModule := ctx.ModuleForTests("libsnapshot.vendor_static.28.arm64", staticCfiVariant).Module().(*Module)
 	assertString(t, staticCfiModule.outputFile.Path().Base(), "libsnapshot.cfi.a")
+
+	staticHwasanModule := ctx.ModuleForTests("libsnapshot.vendor_static.28.arm64", staticHwasanVariant).Module().(*Module)
+	assertString(t, staticHwasanModule.outputFile.Path().Base(), "libsnapshot.hwasan.a")
+
+	staticHwasanCfiModule := ctx.ModuleForTests("libsnapshot.vendor_static.28.arm64", staticHwasanCfiVariant).Module().(*Module)
+	if !staticHwasanCfiModule.HiddenFromMake() || !staticHwasanCfiModule.PreventInstall() {
+		t.Errorf("Hwasan and Cfi cannot enabled at the same time.")
+	}
 }
 
 func TestVendorSnapshotExclude(t *testing.T) {
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index 3bc311b..029bbb4 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -629,40 +629,41 @@
 // symlink tree creation binary. Then the latter would not need to depend on
 // the very heavy-weight machinery of soong_build .
 func runSymlinkForestCreation(configuration android.Config, ctx *android.Context, extraNinjaDeps []string, metricsDir string) string {
-	var ninjaDeps []string
-	ninjaDeps = append(ninjaDeps, extraNinjaDeps...)
-
-	generatedRoot := shared.JoinPath(configuration.SoongOutDir(), "bp2build")
-	workspaceRoot := shared.JoinPath(configuration.SoongOutDir(), "workspace")
-
-	excludes := bazelArtifacts()
-
-	if outDir[0] != '/' {
-		excludes = append(excludes, outDir)
-	}
-
-	existingBazelRelatedFiles, err := getExistingBazelRelatedFiles(topDir)
-	if err != nil {
-		fmt.Fprintf(os.Stderr, "Error determining existing Bazel-related files: %s\n", err)
-		os.Exit(1)
-	}
-
-	pathsToIgnoredBuildFiles := getPathsToIgnoredBuildFiles(configuration.Bp2buildPackageConfig, topDir, existingBazelRelatedFiles, configuration.IsEnvTrue("BP2BUILD_VERBOSE"))
-	excludes = append(excludes, pathsToIgnoredBuildFiles...)
-	excludes = append(excludes, getTemporaryExcludes()...)
-
-	// PlantSymlinkForest() returns all the directories that were readdir()'ed.
-	// Such a directory SHOULD be added to `ninjaDeps` so that a child directory
-	// or file created/deleted under it would trigger an update of the symlink
-	// forest.
 	ctx.EventHandler.Do("symlink_forest", func() {
-		symlinkForestDeps := bp2build.PlantSymlinkForest(
-			configuration.IsEnvTrue("BP2BUILD_VERBOSE"), topDir, workspaceRoot, generatedRoot, excludes)
-		ninjaDeps = append(ninjaDeps, symlinkForestDeps...)
-	})
+		var ninjaDeps []string
+		ninjaDeps = append(ninjaDeps, extraNinjaDeps...)
 
-	writeDepFile(symlinkForestMarker, ctx.EventHandler, ninjaDeps)
-	touch(shared.JoinPath(topDir, symlinkForestMarker))
+		generatedRoot := shared.JoinPath(configuration.SoongOutDir(), "bp2build")
+		workspaceRoot := shared.JoinPath(configuration.SoongOutDir(), "workspace")
+
+		excludes := bazelArtifacts()
+
+		if outDir[0] != '/' {
+			excludes = append(excludes, outDir)
+		}
+
+		existingBazelRelatedFiles, err := getExistingBazelRelatedFiles(topDir)
+		if err != nil {
+			fmt.Fprintf(os.Stderr, "Error determining existing Bazel-related files: %s\n", err)
+			os.Exit(1)
+		}
+
+		pathsToIgnoredBuildFiles := getPathsToIgnoredBuildFiles(configuration.Bp2buildPackageConfig, topDir, existingBazelRelatedFiles, configuration.IsEnvTrue("BP2BUILD_VERBOSE"))
+		excludes = append(excludes, pathsToIgnoredBuildFiles...)
+		excludes = append(excludes, getTemporaryExcludes()...)
+
+		// PlantSymlinkForest() returns all the directories that were readdir()'ed.
+		// Such a directory SHOULD be added to `ninjaDeps` so that a child directory
+		// or file created/deleted under it would trigger an update of the symlink forest.
+		ctx.EventHandler.Do("plant", func() {
+			symlinkForestDeps := bp2build.PlantSymlinkForest(
+				configuration.IsEnvTrue("BP2BUILD_VERBOSE"), topDir, workspaceRoot, generatedRoot, excludes)
+			ninjaDeps = append(ninjaDeps, symlinkForestDeps...)
+		})
+
+		writeDepFile(symlinkForestMarker, ctx.EventHandler, ninjaDeps)
+		touch(shared.JoinPath(topDir, symlinkForestMarker))
+	})
 	codegenMetrics := bp2build.ReadCodegenMetrics(metricsDir)
 	if codegenMetrics == nil {
 		m := bp2build.CreateCodegenMetrics()
diff --git a/java/Android.bp b/java/Android.bp
index 9d63319..27a0a38 100644
--- a/java/Android.bp
+++ b/java/Android.bp
@@ -104,6 +104,7 @@
         "plugin_test.go",
         "prebuilt_apis_test.go",
         "proto_test.go",
+        "resourceshrinker_test.go",
         "rro_test.go",
         "sdk_test.go",
         "sdk_library_test.go",
diff --git a/java/base.go b/java/base.go
index 5d24981..55d77dc 100644
--- a/java/base.go
+++ b/java/base.go
@@ -868,7 +868,7 @@
 	flags = append(flags, genAidlIncludeFlags(ctx, aidlSrcs, includeDirs))
 
 	sdkVersion := (j.SdkVersion(ctx)).Kind
-	defaultTrace := ((sdkVersion == android.SdkSystemServer) || (sdkVersion == android.SdkCore) || (sdkVersion == android.SdkCorePlatform) || (sdkVersion == android.SdkModule))
+	defaultTrace := ((sdkVersion == android.SdkSystemServer) || (sdkVersion == android.SdkCore) || (sdkVersion == android.SdkCorePlatform) || (sdkVersion == android.SdkModule) || (sdkVersion == android.SdkSystem))
 	if proptools.BoolDefault(j.deviceProperties.Aidl.Generate_traces, defaultTrace) {
 		flags = append(flags, "-t")
 	}
diff --git a/java/dex.go b/java/dex.go
index de36b18..40ee99d 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -63,6 +63,7 @@
 		// classes referenced by the app manifest.  Defaults to false.
 		No_aapt_flags *bool
 
+		// If true, optimize for size by removing unused resources. Defaults to false.
 		Shrink_resources *bool
 
 		// Flags to pass to proguard.
diff --git a/java/resourceshrinker_test.go b/java/resourceshrinker_test.go
new file mode 100644
index 0000000..3bbf116
--- /dev/null
+++ b/java/resourceshrinker_test.go
@@ -0,0 +1,53 @@
+// Copyright 2022 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+	"testing"
+
+	"android/soong/android"
+)
+
+func TestShrinkResourcesArgs(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		PrepareForTestWithJavaDefaultModules,
+	).RunTestWithBp(t, `
+		android_app {
+			name: "app_shrink",
+			platform_apis: true,
+			optimize: {
+				shrink_resources: true,
+			}
+		}
+
+		android_app {
+			name: "app_no_shrink",
+			platform_apis: true,
+			optimize: {
+				shrink_resources: false,
+			}
+		}
+	`)
+
+	appShrink := result.ModuleForTests("app_shrink", "android_common")
+	appShrinkResources := appShrink.Rule("shrinkResources")
+	android.AssertStringDoesContain(t, "expected shrinker.xml in app_shrink resource shrinker flags",
+		appShrinkResources.Args["raw_resources"], "shrinker.xml")
+
+	appNoShrink := result.ModuleForTests("app_no_shrink", "android_common")
+	if appNoShrink.MaybeRule("shrinkResources").Rule != nil {
+		t.Errorf("unexpected shrinkResources rule for app_no_shrink")
+	}
+}
diff --git a/java/robolectric.go b/java/robolectric.go
index 2cb0798..1d56708 100644
--- a/java/robolectric.go
+++ b/java/robolectric.go
@@ -327,7 +327,7 @@
 
 func (r *robolectricTest) writeTestRunner(w io.Writer, module, name string, tests []string) {
 	fmt.Fprintln(w, "")
-	fmt.Fprintln(w, "include $(CLEAR_VARS)")
+	fmt.Fprintln(w, "include $(CLEAR_VARS)", " # java.robolectricTest")
 	fmt.Fprintln(w, "LOCAL_MODULE :=", name)
 	fmt.Fprintln(w, "LOCAL_JAVA_LIBRARIES :=", module)
 	fmt.Fprintln(w, "LOCAL_JAVA_LIBRARIES += ", strings.Join(r.libs, " "))
diff --git a/java/testing.go b/java/testing.go
index 49430ee..ccbb638 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -74,6 +74,7 @@
 		// Needed for R8 rules on apps
 		"build/make/core/proguard.flags":             nil,
 		"build/make/core/proguard_basic_keeps.flags": nil,
+		"prebuilts/cmdline-tools/shrinker.xml":       nil,
 	}.AddToFixture(),
 )
 
diff --git a/phony/phony.go b/phony/phony.go
index a31d402..760b79b 100644
--- a/phony/phony.go
+++ b/phony/phony.go
@@ -49,7 +49,7 @@
 func (p *phony) AndroidMk() android.AndroidMkData {
 	return android.AndroidMkData{
 		Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
-			fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)")
+			fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)", " # phony.phony")
 			fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir)
 			fmt.Fprintln(w, "LOCAL_MODULE :=", name)
 			data.Entries.WriteLicenseVariables(w)
diff --git a/sysprop/sysprop_library.go b/sysprop/sysprop_library.go
index 1f0d28d..0edbb7c 100644
--- a/sysprop/sysprop_library.go
+++ b/sysprop/sysprop_library.go
@@ -336,8 +336,8 @@
 		Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
 			// sysprop_library module itself is defined as a FAKE module to perform API check.
 			// Actual implementation libraries are created on LoadHookMutator
-			fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)")
-			fmt.Fprintf(w, "LOCAL_MODULE := %s\n", m.Name())
+			fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)", " # sysprop.syspropLibrary")
+			fmt.Fprintln(w, "LOCAL_MODULE :=", m.Name())
 			data.Entries.WriteLicenseVariables(w)
 			fmt.Fprintf(w, "LOCAL_MODULE_CLASS := FAKE\n")
 			fmt.Fprintf(w, "LOCAL_MODULE_TAGS := optional\n")
diff --git a/ui/build/soong.go b/ui/build/soong.go
index abaf5ae..837f0a4 100644
--- a/ui/build/soong.go
+++ b/ui/build/soong.go
@@ -18,7 +18,6 @@
 	"errors"
 	"fmt"
 	"io/fs"
-	"io/ioutil"
 	"os"
 	"path/filepath"
 	"strconv"
@@ -56,13 +55,13 @@
 	bootstrapEpoch = 1
 )
 
-func writeEnvironmentFile(ctx Context, envFile string, envDeps map[string]string) error {
+func writeEnvironmentFile(_ Context, envFile string, envDeps map[string]string) error {
 	data, err := shared.EnvFileContents(envDeps)
 	if err != nil {
 		return err
 	}
 
-	return ioutil.WriteFile(envFile, data, 0644)
+	return os.WriteFile(envFile, data, 0644)
 }
 
 // This uses Android.bp files and various tools to generate <builddir>/build.ninja.
@@ -141,7 +140,7 @@
 	if exists, err := fileExists(path); err != nil {
 		ctx.Fatalf("Failed to check if file '%s' exists: %s", path, err)
 	} else if !exists {
-		err = ioutil.WriteFile(path, nil, 0666)
+		err = os.WriteFile(path, nil, 0666)
 		if err != nil {
 			ctx.Fatalf("Failed to create empty file '%s': %s", path, err)
 		}
@@ -157,24 +156,28 @@
 	return true, nil
 }
 
-func primaryBuilderInvocation(
-	config Config,
-	name string,
-	output string,
-	specificArgs []string,
-	description string) bootstrap.PrimaryBuilderInvocation {
+type PrimaryBuilderFactory struct {
+	name         string
+	description  string
+	config       Config
+	output       string
+	specificArgs []string
+	debugPort    string
+}
+
+func (pb PrimaryBuilderFactory) primaryBuilderInvocation() bootstrap.PrimaryBuilderInvocation {
 	commonArgs := make([]string, 0, 0)
 
-	if !config.skipSoongTests {
+	if !pb.config.skipSoongTests {
 		commonArgs = append(commonArgs, "-t")
 	}
 
-	commonArgs = append(commonArgs, "-l", filepath.Join(config.FileListDir(), "Android.bp.list"))
+	commonArgs = append(commonArgs, "-l", filepath.Join(pb.config.FileListDir(), "Android.bp.list"))
 	invocationEnv := make(map[string]string)
-	if os.Getenv("SOONG_DELVE") != "" {
+	if pb.debugPort != "" {
 		//debug mode
-		commonArgs = append(commonArgs, "--delve_listen", os.Getenv("SOONG_DELVE"))
-		commonArgs = append(commonArgs, "--delve_path", shared.ResolveDelveBinary())
+		commonArgs = append(commonArgs, "--delve_listen", pb.debugPort,
+			"--delve_path", shared.ResolveDelveBinary())
 		// GODEBUG=asyncpreemptoff=1 disables the preemption of goroutines. This
 		// is useful because the preemption happens by sending SIGURG to the OS
 		// thread hosting the goroutine in question and each signal results in
@@ -188,26 +191,26 @@
 	}
 
 	var allArgs []string
-	allArgs = append(allArgs, specificArgs...)
+	allArgs = append(allArgs, pb.specificArgs...)
 	allArgs = append(allArgs,
-		"--globListDir", name,
-		"--globFile", config.NamedGlobFile(name))
+		"--globListDir", pb.name,
+		"--globFile", pb.config.NamedGlobFile(pb.name))
 
 	allArgs = append(allArgs, commonArgs...)
-	allArgs = append(allArgs, environmentArgs(config, name)...)
+	allArgs = append(allArgs, environmentArgs(pb.config, pb.name)...)
 	if profileCpu := os.Getenv("SOONG_PROFILE_CPU"); profileCpu != "" {
-		allArgs = append(allArgs, "--cpuprofile", profileCpu+"."+name)
+		allArgs = append(allArgs, "--cpuprofile", profileCpu+"."+pb.name)
 	}
 	if profileMem := os.Getenv("SOONG_PROFILE_MEM"); profileMem != "" {
-		allArgs = append(allArgs, "--memprofile", profileMem+"."+name)
+		allArgs = append(allArgs, "--memprofile", profileMem+"."+pb.name)
 	}
 	allArgs = append(allArgs, "Android.bp")
 
 	return bootstrap.PrimaryBuilderInvocation{
 		Inputs:      []string{"Android.bp"},
-		Outputs:     []string{output},
+		Outputs:     []string{pb.output},
 		Args:        allArgs,
-		Description: description,
+		Description: pb.description,
 		// NB: Changing the value of this environment variable will not result in a
 		// rebuild. The bootstrap Ninja file will change, but apparently Ninja does
 		// not consider changing the pool specified in a statement a change that's
@@ -270,90 +273,117 @@
 	if config.bazelStagingMode {
 		mainSoongBuildExtraArgs = append(mainSoongBuildExtraArgs, "--bazel-mode-staging")
 	}
-
-	mainSoongBuildInvocation := primaryBuilderInvocation(
-		config,
-		soongBuildTag,
-		config.SoongNinjaFile(),
-		mainSoongBuildExtraArgs,
-		fmt.Sprintf("analyzing Android.bp files and generating ninja file at %s", config.SoongNinjaFile()),
-	)
-
-	if config.BazelBuildEnabled() {
-		// Mixed builds call Bazel from soong_build and they therefore need the
-		// Bazel workspace to be available. Make that so by adding a dependency on
-		// the bp2build marker file to the action that invokes soong_build .
-		mainSoongBuildInvocation.OrderOnlyInputs = append(mainSoongBuildInvocation.OrderOnlyInputs,
-			config.Bp2BuildWorkspaceMarkerFile())
-	}
-
-	bp2buildInvocation := primaryBuilderInvocation(
-		config,
-		bp2buildFilesTag,
-		config.Bp2BuildFilesMarkerFile(),
-		[]string{
-			"--bp2build_marker", config.Bp2BuildFilesMarkerFile(),
-		},
-		fmt.Sprintf("converting Android.bp files to BUILD files at %s/bp2build", config.SoongOutDir()),
-	)
-
-	bp2buildWorkspaceInvocation := primaryBuilderInvocation(
-		config,
-		bp2buildWorkspaceTag,
-		config.Bp2BuildWorkspaceMarkerFile(),
-		[]string{
-			"--symlink_forest_marker", config.Bp2BuildWorkspaceMarkerFile(),
-		},
-		fmt.Sprintf("Creating Bazel symlink forest"),
-	)
-
-	bp2buildWorkspaceInvocation.Inputs = append(bp2buildWorkspaceInvocation.Inputs,
-		config.Bp2BuildFilesMarkerFile(), filepath.Join(config.FileListDir(), "bazel.list"))
-
-	jsonModuleGraphInvocation := primaryBuilderInvocation(
-		config,
-		jsonModuleGraphTag,
-		config.ModuleGraphFile(),
-		[]string{
-			"--module_graph_file", config.ModuleGraphFile(),
-			"--module_actions_file", config.ModuleActionsFile(),
-		},
-		fmt.Sprintf("generating the Soong module graph at %s", config.ModuleGraphFile()),
-	)
-
 	queryviewDir := filepath.Join(config.SoongOutDir(), "queryview")
-	queryviewInvocation := primaryBuilderInvocation(
-		config,
-		queryviewTag,
-		config.QueryviewMarkerFile(),
-		[]string{
-			"--bazel_queryview_dir", queryviewDir,
-		},
-		fmt.Sprintf("generating the Soong module graph as a Bazel workspace at %s", queryviewDir),
-	)
-
 	// The BUILD files will be generated in out/soong/.api_bp2build (no symlinks to src files)
 	// The final workspace will be generated in out/soong/api_bp2build
 	apiBp2buildDir := filepath.Join(config.SoongOutDir(), ".api_bp2build")
-	apiBp2buildInvocation := primaryBuilderInvocation(
-		config,
-		apiBp2buildTag,
-		config.ApiBp2buildMarkerFile(),
-		[]string{
-			"--bazel_api_bp2build_dir", apiBp2buildDir,
-		},
-		fmt.Sprintf("generating BUILD files for API contributions at %s", apiBp2buildDir),
-	)
 
-	soongDocsInvocation := primaryBuilderInvocation(
-		config,
-		soongDocsTag,
-		config.SoongDocsHtml(),
-		[]string{
-			"--soong_docs", config.SoongDocsHtml(),
+	pbfs := []PrimaryBuilderFactory{
+		{
+			name:         soongBuildTag,
+			description:  fmt.Sprintf("analyzing Android.bp files and generating ninja file at %s", config.SoongNinjaFile()),
+			config:       config,
+			output:       config.SoongNinjaFile(),
+			specificArgs: mainSoongBuildExtraArgs,
 		},
-		fmt.Sprintf("generating Soong docs at %s", config.SoongDocsHtml()),
-	)
+		{
+			name:         bp2buildFilesTag,
+			description:  fmt.Sprintf("converting Android.bp files to BUILD files at %s/bp2build", config.SoongOutDir()),
+			config:       config,
+			output:       config.Bp2BuildFilesMarkerFile(),
+			specificArgs: []string{"--bp2build_marker", config.Bp2BuildFilesMarkerFile()},
+		},
+		{
+			name:         bp2buildWorkspaceTag,
+			description:  "Creating Bazel symlink forest",
+			config:       config,
+			output:       config.Bp2BuildWorkspaceMarkerFile(),
+			specificArgs: []string{"--symlink_forest_marker", config.Bp2BuildWorkspaceMarkerFile()},
+		},
+		{
+			name:        jsonModuleGraphTag,
+			description: fmt.Sprintf("generating the Soong module graph at %s", config.ModuleGraphFile()),
+			config:      config,
+			output:      config.ModuleGraphFile(),
+			specificArgs: []string{
+				"--module_graph_file", config.ModuleGraphFile(),
+				"--module_actions_file", config.ModuleActionsFile(),
+			},
+		},
+		{
+			name:         queryviewTag,
+			description:  fmt.Sprintf("generating the Soong module graph as a Bazel workspace at %s", queryviewDir),
+			config:       config,
+			output:       config.QueryviewMarkerFile(),
+			specificArgs: []string{"--bazel_queryview_dir", queryviewDir},
+		},
+		{
+			name:         apiBp2buildTag,
+			description:  fmt.Sprintf("generating BUILD files for API contributions at %s", apiBp2buildDir),
+			config:       config,
+			output:       config.ApiBp2buildMarkerFile(),
+			specificArgs: []string{"--bazel_api_bp2build_dir", apiBp2buildDir},
+		},
+		{
+			name:         soongDocsTag,
+			description:  fmt.Sprintf("generating Soong docs at %s", config.SoongDocsHtml()),
+			config:       config,
+			output:       config.SoongDocsHtml(),
+			specificArgs: []string{"--soong_docs", config.SoongDocsHtml()},
+		},
+	}
+
+	// Figure out which invocations will be run under the debugger:
+	//   * SOONG_DELVE if set specifies listening port
+	//   * SOONG_DELVE_STEPS if set specifies specific invocations to be debugged, otherwise all are
+	debuggedInvocations := make(map[string]bool)
+	delvePort := os.Getenv("SOONG_DELVE")
+	if delvePort != "" {
+		if steps := os.Getenv("SOONG_DELVE_STEPS"); steps != "" {
+			var validSteps []string
+			for _, pbf := range pbfs {
+				debuggedInvocations[pbf.name] = false
+				validSteps = append(validSteps, pbf.name)
+
+			}
+			for _, step := range strings.Split(steps, ",") {
+				if _, ok := debuggedInvocations[step]; ok {
+					debuggedInvocations[step] = true
+				} else {
+					ctx.Fatalf("SOONG_DELVE_STEPS contains unknown soong_build step %s\n"+
+						"Valid steps are %v", step, validSteps)
+				}
+			}
+		} else {
+			//  SOONG_DELVE_STEPS is not set, run all steps in the debugger
+			for _, pbf := range pbfs {
+				debuggedInvocations[pbf.name] = true
+			}
+		}
+	}
+
+	var invocations []bootstrap.PrimaryBuilderInvocation
+	for _, pbf := range pbfs {
+		if debuggedInvocations[pbf.name] {
+			pbf.debugPort = delvePort
+		}
+		pbi := pbf.primaryBuilderInvocation()
+		// Some invocations require adjustment:
+		switch pbf.name {
+		case soongBuildTag:
+			if config.BazelBuildEnabled() {
+				// Mixed builds call Bazel from soong_build and they therefore need the
+				// Bazel workspace to be available. Make that so by adding a dependency on
+				// the bp2build marker file to the action that invokes soong_build .
+				pbi.OrderOnlyInputs = append(pbi.OrderOnlyInputs, config.Bp2BuildWorkspaceMarkerFile())
+			}
+		case bp2buildWorkspaceTag:
+			pbi.Inputs = append(pbi.Inputs,
+				config.Bp2BuildFilesMarkerFile(),
+				filepath.Join(config.FileListDir(), "bazel.list"))
+		}
+		invocations = append(invocations, pbi)
+	}
 
 	// The glob .ninja files are subninja'd. However, they are generated during
 	// the build itself so we write an empty file if the file does not exist yet
@@ -362,11 +392,11 @@
 		writeEmptyFile(ctx, globFile)
 	}
 
-	var blueprintArgs bootstrap.Args
-
-	blueprintArgs.ModuleListFile = filepath.Join(config.FileListDir(), "Android.bp.list")
-	blueprintArgs.OutFile = shared.JoinPath(config.SoongOutDir(), "bootstrap.ninja")
-	blueprintArgs.EmptyNinjaFile = false
+	blueprintArgs := bootstrap.Args{
+		ModuleListFile: filepath.Join(config.FileListDir(), "Android.bp.list"),
+		OutFile:        shared.JoinPath(config.SoongOutDir(), "bootstrap.ninja"),
+		EmptyNinjaFile: false,
+	}
 
 	blueprintCtx := blueprint.NewContext()
 	blueprintCtx.SetIgnoreUnknownModuleTypes(true)
@@ -376,16 +406,9 @@
 		outDir:      config.OutDir(),
 		runGoTests:  !config.skipSoongTests,
 		// If we want to debug soong_build, we need to compile it for debugging
-		debugCompilation: os.Getenv("SOONG_DELVE") != "",
-		subninjas:        bootstrapGlobFileList(config),
-		primaryBuilderInvocations: []bootstrap.PrimaryBuilderInvocation{
-			mainSoongBuildInvocation,
-			bp2buildInvocation,
-			bp2buildWorkspaceInvocation,
-			jsonModuleGraphInvocation,
-			queryviewInvocation,
-			apiBp2buildInvocation,
-			soongDocsInvocation},
+		debugCompilation:          delvePort != "",
+		subninjas:                 bootstrapGlobFileList(config),
+		primaryBuilderInvocations: invocations,
 	}
 
 	// since `bootstrap.ninja` is regenerated unconditionally, we ignore the deps, i.e. little