Merge "Switch host native tests to -Og" into main
diff --git a/android/apex.go b/android/apex.go
index 683e501..ecab8e3 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -610,9 +610,15 @@
 		return ""
 	}
 
-	// If this module has no apex variations the use the platform variation.
 	if len(apexInfos) == 0 {
-		return ""
+		if ctx.IsAddingDependency() {
+			// If this module has no apex variations we can't do any mapping on the incoming variation, just return it
+			// and let the caller get a "missing variant" error.
+			return incomingVariation
+		} else {
+			// If this module has no apex variations the use the platform variation.
+			return ""
+		}
 	}
 
 	// Convert the list of apex infos into from the AllApexInfoProvider into the merged list
diff --git a/android/arch_list.go b/android/arch_list.go
index f4409a9..4233456 100644
--- a/android/arch_list.go
+++ b/android/arch_list.go
@@ -103,6 +103,7 @@
 		"kryo385",
 		"exynos-m1",
 		"exynos-m2",
+		"oryon",
 	},
 	X86:    {},
 	X86_64: {},
diff --git a/android/license_metadata.go b/android/license_metadata.go
index 3fea029..8056189 100644
--- a/android/license_metadata.go
+++ b/android/license_metadata.go
@@ -45,8 +45,7 @@
 	}
 
 	var outputFiles Paths
-	if outputFileProducer, ok := ctx.Module().(OutputFileProducer); ok {
-		outputFiles, _ = outputFileProducer.OutputFiles("")
+	if outputFiles, err := outputFilesForModule(ctx, ctx.Module(), ""); err == nil {
 		outputFiles = PathsIfNonNil(outputFiles...)
 	}
 
diff --git a/android/module.go b/android/module.go
index 66f6e1b..9f7cb37 100644
--- a/android/module.go
+++ b/android/module.go
@@ -1746,7 +1746,11 @@
 	}
 }
 
-func (m *ModuleBase) archModuleContextFactory(ctx blueprint.IncomingTransitionContext) archModuleContext {
+type archModuleContextFactoryContext interface {
+	Config() interface{}
+}
+
+func (m *ModuleBase) archModuleContextFactory(ctx archModuleContextFactoryContext) archModuleContext {
 	config := ctx.Config().(Config)
 	target := m.Target()
 	primaryArch := false
@@ -2491,21 +2495,14 @@
 	if outputFilesFromProvider != nil || err != OutputFilesProviderNotSet {
 		return outputFilesFromProvider, err
 	}
-	if outputFileProducer, ok := module.(OutputFileProducer); ok {
-		paths, err := outputFileProducer.OutputFiles(tag)
-		if err != nil {
-			return nil, fmt.Errorf("failed to get output file from module %q at tag %q: %s",
-				pathContextName(ctx, module), tag, err.Error())
-		}
-		return paths, nil
-	} else if sourceFileProducer, ok := module.(SourceFileProducer); ok {
+	if sourceFileProducer, ok := module.(SourceFileProducer); ok {
 		if tag != "" {
-			return nil, fmt.Errorf("module %q is a SourceFileProducer, not an OutputFileProducer, and so does not support tag %q", pathContextName(ctx, module), tag)
+			return nil, fmt.Errorf("module %q is a SourceFileProducer, which does not support tag %q", pathContextName(ctx, module), tag)
 		}
 		paths := sourceFileProducer.Srcs()
 		return paths, nil
 	} else {
-		return nil, fmt.Errorf("module %q is not an OutputFileProducer or SourceFileProducer", pathContextName(ctx, module))
+		return nil, fmt.Errorf("module %q is not a SourceFileProducer or having valid output file for tag %q", pathContextName(ctx, module), tag)
 	}
 }
 
@@ -2519,7 +2516,12 @@
 	var outputFiles OutputFilesInfo
 	fromProperty := false
 
-	if mctx, isMctx := ctx.(ModuleContext); isMctx {
+	type OutputFilesProviderModuleContext interface {
+		OtherModuleProviderContext
+		Module() Module
+	}
+
+	if mctx, isMctx := ctx.(OutputFilesProviderModuleContext); isMctx {
 		if mctx.Module() != module {
 			outputFiles, _ = OtherModuleProvider(mctx, module, OutputFilesProvider)
 		} else {
@@ -2533,7 +2535,6 @@
 	// TODO: Add a check for skipped context
 
 	if outputFiles.isEmpty() {
-		// TODO: Add a check for param module not having OutputFilesProvider set
 		return nil, OutputFilesProviderNotSet
 	}
 
diff --git a/android/module_test.go b/android/module_test.go
index 1f3db5c..922ea21 100644
--- a/android/module_test.go
+++ b/android/module_test.go
@@ -935,31 +935,54 @@
 	}
 }
 
-type fakeBlueprintModule struct{}
-
-func (fakeBlueprintModule) Name() string { return "foo" }
-
-func (fakeBlueprintModule) GenerateBuildActions(blueprint.ModuleContext) {}
-
 type sourceProducerTestModule struct {
-	fakeBlueprintModule
-	source Path
+	ModuleBase
+	props struct {
+		// A represents the source file
+		A string
+	}
 }
 
-func (s sourceProducerTestModule) Srcs() Paths { return Paths{s.source} }
-
-type outputFileProducerTestModule struct {
-	fakeBlueprintModule
-	output map[string]Path
-	error  map[string]error
+func sourceProducerTestModuleFactory() Module {
+	module := &sourceProducerTestModule{}
+	module.AddProperties(&module.props)
+	InitAndroidModule(module)
+	return module
 }
 
-func (o outputFileProducerTestModule) OutputFiles(tag string) (Paths, error) {
-	return PathsIfNonNil(o.output[tag]), o.error[tag]
+func (s sourceProducerTestModule) GenerateAndroidBuildActions(ModuleContext) {}
+
+func (s sourceProducerTestModule) Srcs() Paths { return PathsForTesting(s.props.A) }
+
+type outputFilesTestModule struct {
+	ModuleBase
+	props struct {
+		// A represents the tag
+		A string
+		// B represents the output file for tag A
+		B string
+	}
+}
+
+func outputFilesTestModuleFactory() Module {
+	module := &outputFilesTestModule{}
+	module.AddProperties(&module.props)
+	InitAndroidModule(module)
+	return module
+}
+
+func (o outputFilesTestModule) GenerateAndroidBuildActions(ctx ModuleContext) {
+	if o.props.A != "" || o.props.B != "" {
+		ctx.SetOutputFiles(PathsForTesting(o.props.B), o.props.A)
+	}
+	// This is to simulate the case that some module uses an object to set its
+	// OutputFilesProvider, but the object itself is empty.
+	ctx.SetOutputFiles(Paths{}, "missing")
 }
 
 type pathContextAddMissingDependenciesWrapper struct {
 	PathContext
+	OtherModuleProviderContext
 	missingDeps []string
 }
 
@@ -970,52 +993,87 @@
 	return module.Name()
 }
 
+func (p *pathContextAddMissingDependenciesWrapper) Module() Module { return nil }
+
 func TestOutputFileForModule(t *testing.T) {
 	testcases := []struct {
 		name        string
-		module      blueprint.Module
+		bp          string
 		tag         string
-		env         map[string]string
-		config      func(*config)
 		expected    string
 		missingDeps []string
+		env         map[string]string
+		config      func(*config)
 	}{
 		{
-			name:     "SourceFileProducer",
-			module:   &sourceProducerTestModule{source: PathForTesting("foo.txt")},
-			expected: "foo.txt",
+			name: "SourceFileProducer",
+			bp: `spt_module {
+					name: "test_module",
+					a: "spt.txt",
+				}
+			`,
+			tag:      "",
+			expected: "spt.txt",
 		},
 		{
-			name:     "OutputFileProducer",
-			module:   &outputFileProducerTestModule{output: map[string]Path{"": PathForTesting("foo.txt")}},
-			expected: "foo.txt",
+			name: "OutputFileProviderEmptyStringTag",
+			bp: `oft_module {
+					name: "test_module",
+					a: "",
+					b: "empty.txt",
+				}
+		`,
+			tag:      "",
+			expected: "empty.txt",
 		},
 		{
-			name:     "OutputFileProducer_tag",
-			module:   &outputFileProducerTestModule{output: map[string]Path{"foo": PathForTesting("foo.txt")}},
+			name: "OutputFileProviderTag",
+			bp: `oft_module {
+					name: "test_module",
+					a: "foo",
+					b: "foo.txt",
+				}
+			`,
 			tag:      "foo",
 			expected: "foo.txt",
 		},
 		{
-			name: "OutputFileProducer_AllowMissingDependencies",
+			name: "OutputFileAllowMissingDependencies",
+			bp: `oft_module {
+				name: "test_module",
+			}
+		`,
+			tag:         "missing",
+			expected:    "missing_output_file/test_module",
+			missingDeps: []string{"test_module"},
 			config: func(config *config) {
 				config.TestProductVariables.Allow_missing_dependencies = boolPtr(true)
 			},
-			module:      &outputFileProducerTestModule{},
-			missingDeps: []string{"foo"},
-			expected:    "missing_output_file/foo",
 		},
 	}
+
 	for _, tt := range testcases {
-		config := TestConfig(buildDir, tt.env, "", nil)
-		if tt.config != nil {
-			tt.config(config.config)
-		}
-		ctx := &pathContextAddMissingDependenciesWrapper{
-			PathContext: PathContextForTesting(config),
-		}
-		got := OutputFileForModule(ctx, tt.module, tt.tag)
-		AssertPathRelativeToTopEquals(t, "expected source path", tt.expected, got)
-		AssertArrayString(t, "expected missing deps", tt.missingDeps, ctx.missingDeps)
+		t.Run(tt.name, func(t *testing.T) {
+			result := GroupFixturePreparers(
+				PrepareForTestWithDefaults,
+				FixtureRegisterWithContext(func(ctx RegistrationContext) {
+					ctx.RegisterModuleType("spt_module", sourceProducerTestModuleFactory)
+					ctx.RegisterModuleType("oft_module", outputFilesTestModuleFactory)
+				}),
+				FixtureWithRootAndroidBp(tt.bp),
+			).RunTest(t)
+
+			config := TestConfig(buildDir, tt.env, tt.bp, nil)
+			if tt.config != nil {
+				tt.config(config.config)
+			}
+			ctx := &pathContextAddMissingDependenciesWrapper{
+				PathContext:                PathContextForTesting(config),
+				OtherModuleProviderContext: result.TestContext.OtherModuleProviderAdaptor(),
+			}
+			got := OutputFileForModule(ctx, result.ModuleForTests("test_module", "").Module(), tt.tag)
+			AssertPathRelativeToTopEquals(t, "expected output path", tt.expected, got)
+			AssertArrayString(t, "expected missing deps", tt.missingDeps, ctx.missingDeps)
+		})
 	}
 }
diff --git a/android/mutator.go b/android/mutator.go
index 440b906..b81dd12 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -400,6 +400,12 @@
 	Config() Config
 
 	DeviceConfig() DeviceConfig
+
+	// IsAddingDependency returns true if the transition is being called while adding a dependency
+	// after the transition mutator has already run, or false if it is being called when the transition
+	// mutator is running.  This should be used sparingly, all uses will have to be removed in order
+	// to support creating variants on demand.
+	IsAddingDependency() bool
 }
 
 type OutgoingTransitionContext interface {
@@ -574,6 +580,10 @@
 	return DeviceConfig{c.bp.Config().(Config).deviceConfig}
 }
 
+func (c *incomingTransitionContextImpl) IsAddingDependency() bool {
+	return c.bp.IsAddingDependency()
+}
+
 func (c *incomingTransitionContextImpl) provider(provider blueprint.AnyProviderKey) (any, bool) {
 	return c.bp.Provider(provider)
 }
diff --git a/apex/apex.go b/apex/apex.go
index a2c1f2c..10fe372 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -1363,7 +1363,7 @@
 var _ cc.Coverage = (*apexBundle)(nil)
 
 // Implements cc.Coverage
-func (a *apexBundle) IsNativeCoverageNeeded(ctx android.IncomingTransitionContext) bool {
+func (a *apexBundle) IsNativeCoverageNeeded(ctx cc.IsNativeCoverageNeededContext) bool {
 	return ctx.DeviceConfig().NativeCoverageEnabled()
 }
 
@@ -2095,20 +2095,10 @@
 			}
 		case testTag:
 			if ccTest, ok := child.(*cc.Module); ok {
-				if ccTest.IsTestPerSrcAllTestsVariation() {
-					// Multiple-output test module (where `test_per_src: true`).
-					//
-					// `ccTest` is the "" ("all tests") variation of a `test_per_src` module.
-					// We do not add this variation to `filesInfo`, as it has no output;
-					// however, we do add the other variations of this module as indirect
-					// dependencies (see below).
-				} else {
-					// Single-output test module (where `test_per_src: false`).
-					af := apexFileForExecutable(ctx, ccTest)
-					af.class = nativeTest
-					vctx.filesInfo = append(vctx.filesInfo, af)
-					addAconfigFiles(vctx, ctx, child)
-				}
+				af := apexFileForExecutable(ctx, ccTest)
+				af.class = nativeTest
+				vctx.filesInfo = append(vctx.filesInfo, af)
+				addAconfigFiles(vctx, ctx, child)
 				return true // track transitive dependencies
 			} else {
 				ctx.PropertyErrorf("tests", "%q is not a cc module", depName)
@@ -2197,19 +2187,6 @@
 			addAconfigFiles(vctx, ctx, child)
 			return true // track transitive dependencies
 		}
-	} else if cc.IsTestPerSrcDepTag(depTag) {
-		if ch, ok := child.(*cc.Module); ok {
-			af := apexFileForExecutable(ctx, ch)
-			// Handle modules created as `test_per_src` variations of a single test module:
-			// use the name of the generated test binary (`fileToCopy`) instead of the name
-			// of the original test module (`depName`, shared by all `test_per_src`
-			// variations of that module).
-			af.androidMkModuleName = filepath.Base(af.builtFile.String())
-			// these are not considered transitive dep
-			af.transitiveDep = false
-			vctx.filesInfo = append(vctx.filesInfo, af)
-			return true // track transitive dependencies
-		}
 	} else if cc.IsHeaderDepTag(depTag) {
 		// nothing
 	} else if java.IsJniDepTag(depTag) {
diff --git a/apex/apex_test.go b/apex/apex_test.go
index df9db74..15c713b 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -5728,7 +5728,6 @@
 			updatable: false,
 			tests: [
 				"mytest",
-				"mytests",
 			],
 		}
 
@@ -5771,25 +5770,6 @@
 				"testdata/baz"
 			],
 		}
-
-		cc_test {
-			name: "mytests",
-			gtest: false,
-			srcs: [
-				"mytest1.cpp",
-				"mytest2.cpp",
-				"mytest3.cpp",
-			],
-			test_per_src: true,
-			relative_install_path: "test",
-			system_shared_libs: [],
-			static_executable: true,
-			stl: "none",
-			data: [
-				":fg",
-				":fg2",
-			],
-		}
 	`)
 
 	apexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexRule")
@@ -5803,11 +5783,6 @@
 	ensureContains(t, copyCmds, "image.apex/bin/test/baz")
 	ensureContains(t, copyCmds, "image.apex/bin/test/bar/baz")
 
-	// Ensure that test deps built with `test_per_src` are copied into apex.
-	ensureContains(t, copyCmds, "image.apex/bin/test/mytest1")
-	ensureContains(t, copyCmds, "image.apex/bin/test/mytest2")
-	ensureContains(t, copyCmds, "image.apex/bin/test/mytest3")
-
 	// Ensure the module is correctly translated.
 	bundle := ctx.ModuleForTests("myapex", "android_common_myapex").Module().(*apexBundle)
 	data := android.AndroidMkDataForTest(t, ctx, bundle)
@@ -5817,9 +5792,6 @@
 	data.Custom(&builder, name, prefix, "", data)
 	androidMk := builder.String()
 	ensureContains(t, androidMk, "LOCAL_MODULE := mytest.myapex\n")
-	ensureContains(t, androidMk, "LOCAL_MODULE := mytest1.myapex\n")
-	ensureContains(t, androidMk, "LOCAL_MODULE := mytest2.myapex\n")
-	ensureContains(t, androidMk, "LOCAL_MODULE := mytest3.myapex\n")
 	ensureContains(t, androidMk, "LOCAL_MODULE := myapex\n")
 }
 
diff --git a/cc/androidmk.go b/cc/androidmk.go
index 143e86f..4134653 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -351,9 +351,6 @@
 	ctx.subAndroidMk(entries, test.testDecorator)
 
 	entries.Class = "NATIVE_TESTS"
-	if Bool(test.Properties.Test_per_src) {
-		entries.SubName = "_" + String(test.binaryDecorator.Properties.Stem)
-	}
 	entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 		if test.testConfig != nil {
 			entries.SetString("LOCAL_FULL_TEST_CONFIG", test.testConfig.String())
diff --git a/cc/cc.go b/cc/cc.go
index 24f6b83..740be3a 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -51,7 +51,6 @@
 		ctx.BottomUp("sdk", sdkMutator).Parallel()
 		ctx.BottomUp("llndk", llndkMutator).Parallel()
 		ctx.BottomUp("link", LinkageMutator).Parallel()
-		ctx.BottomUp("test_per_src", TestPerSrcMutator).Parallel()
 		ctx.BottomUp("version", versionMutator).Parallel()
 		ctx.BottomUp("begin", BeginMutator).Parallel()
 	})
@@ -799,7 +798,6 @@
 	dataLibDepTag         = dependencyTag{name: "data lib"}
 	dataBinDepTag         = dependencyTag{name: "data bin"}
 	runtimeDepTag         = installDependencyTag{name: "runtime lib"}
-	testPerSrcDepTag      = dependencyTag{name: "test_per_src"}
 	stubImplDepTag        = dependencyTag{name: "stub_impl"}
 	JniFuzzLibTag         = dependencyTag{name: "jni_fuzz_lib_tag"}
 	FdoProfileTag         = dependencyTag{name: "fdo_profile"}
@@ -826,11 +824,6 @@
 	return depTag == runtimeDepTag
 }
 
-func IsTestPerSrcDepTag(depTag blueprint.DependencyTag) bool {
-	ccDepTag, ok := depTag.(dependencyTag)
-	return ok && ccDepTag == testPerSrcDepTag
-}
-
 // Module contains the properties and members used by all C/C++ module types, and implements
 // the blueprint.Module interface.  It delegates to compiler, linker, and installer interfaces
 // to construct the output file.  Behavior can be customized with a Customizer, or "decorator",
@@ -1386,17 +1379,11 @@
 }
 
 func (c *Module) isCfi() bool {
-	if sanitize := c.sanitize; sanitize != nil {
-		return Bool(sanitize.Properties.SanitizeMutated.Cfi)
-	}
-	return false
+	return c.sanitize.isSanitizerEnabled(cfi)
 }
 
 func (c *Module) isFuzzer() bool {
-	if sanitize := c.sanitize; sanitize != nil {
-		return Bool(sanitize.Properties.SanitizeMutated.Fuzzer)
-	}
-	return false
+	return c.sanitize.isSanitizerEnabled(Fuzzer)
 }
 
 func (c *Module) isNDKStubLibrary() bool {
@@ -1786,11 +1773,6 @@
 	return nil
 }
 
-func (c *Module) IsTestPerSrcAllTestsVariation() bool {
-	test, ok := c.linker.(testPerSrc)
-	return ok && test.isAllTestsVariation()
-}
-
 func (c *Module) DataPaths() []android.DataPath {
 	if p, ok := c.installer.(interface {
 		dataPaths() []android.DataPath
@@ -1943,16 +1925,6 @@
 		TopLevelTarget: c.testModule,
 	})
 
-	// Handle the case of a test module split by `test_per_src` mutator.
-	//
-	// The `test_per_src` mutator adds an extra variation named "", depending on all the other
-	// `test_per_src` variations of the test module. Set `outputFile` to an empty path for this
-	// module and return early, as this module does not produce an output file per se.
-	if c.IsTestPerSrcAllTestsVariation() {
-		c.outputFile = android.OptionalPath{}
-		return
-	}
-
 	c.Properties.SubName = GetSubnameProperty(actx, c)
 	apexInfo, _ := android.ModuleProvider(actx, android.ApexInfoProvider)
 	if !apexInfo.IsForPlatform() {
@@ -3758,14 +3730,12 @@
 }
 
 // Overrides ApexModule.IsInstallabeToApex()
-// Only shared/runtime libraries and "test_per_src" tests are installable to APEX.
+// Only shared/runtime libraries .
 func (c *Module) IsInstallableToApex() bool {
 	if lib := c.library; lib != nil {
 		// Stub libs and prebuilt libs in a versioned SDK are not
 		// installable to APEX even though they are shared libs.
 		return lib.shared() && !lib.buildStubs()
-	} else if _, ok := c.linker.(testPerSrc); ok {
-		return true
 	}
 	return false
 }
diff --git a/cc/cc_test_only_property_test.go b/cc/cc_test_only_property_test.go
index c14f34e..972e86b 100644
--- a/cc/cc_test_only_property_test.go
+++ b/cc/cc_test_only_property_test.go
@@ -78,38 +78,6 @@
 	}
 }
 
-func TestTestOnlyValueWithTestPerSrcProp(t *testing.T) {
-	t.Parallel()
-	ctx := android.GroupFixturePreparers(
-		prepareForCcTest,
-	).RunTestWithBp(t, `
-                // These should be test-only
-                cc_test { name: "cc-test",
-                          gtest: false,
-                          test_per_src: true,
-                          srcs: ["foo_test.cpp"],
-                          test_options: { unit_test: false, },
-                         }
-	`)
-
-	// Ensure all variation of test-per-src tests are marked test-only.
-	ctx.VisitAllModules(func(m blueprint.Module) {
-		testOnly := false
-		if provider, ok := android.OtherModuleProvider(ctx.TestContext.OtherModuleProviderAdaptor(), m, android.TestOnlyProviderKey); ok {
-			if provider.TestOnly {
-				testOnly = true
-			}
-		}
-		if module, ok := m.(*Module); ok {
-			if testModule, ok := module.installer.(*testBinary); ok {
-				if !testOnly && *testModule.Properties.Test_per_src {
-					t.Errorf("%v is not test-only but should be", m)
-				}
-			}
-		}
-	})
-}
-
 func TestTestOnlyInTeamsProto(t *testing.T) {
 	t.Parallel()
 	ctx := android.GroupFixturePreparers(
diff --git a/cc/coverage.go b/cc/coverage.go
index f6092e4..a7618dd 100644
--- a/cc/coverage.go
+++ b/cc/coverage.go
@@ -23,26 +23,26 @@
 )
 
 var (
- 	clangCoverageHostLdFlags = []string{
- 		"-Wl,--no-as-needed",
- 		"-Wl,--wrap,open",
- 	}
- 	clangContinuousCoverageFlags = []string{
- 		"-mllvm",
- 		"-runtime-counter-relocation",
- 	}
- 	clangCoverageCFlags = []string{
- 		"-Wno-frame-larger-than=",
- 	}
- 	clangCoverageCommonFlags = []string{
- 		"-fcoverage-mapping",
- 		"-Wno-pass-failed",
- 		"-D__ANDROID_CLANG_COVERAGE__",
- 	}
- 	clangCoverageHWASanFlags = []string{
- 		"-mllvm",
- 		"-hwasan-globals=0",
- 	}
+	clangCoverageHostLdFlags = []string{
+		"-Wl,--no-as-needed",
+		"-Wl,--wrap,open",
+	}
+	clangContinuousCoverageFlags = []string{
+		"-mllvm",
+		"-runtime-counter-relocation",
+	}
+	clangCoverageCFlags = []string{
+		"-Wno-frame-larger-than=",
+	}
+	clangCoverageCommonFlags = []string{
+		"-fcoverage-mapping",
+		"-Wno-pass-failed",
+		"-D__ANDROID_CLANG_COVERAGE__",
+	}
+	clangCoverageHWASanFlags = []string{
+		"-mllvm",
+		"-hwasan-globals=0",
+	}
 )
 
 const profileInstrFlag = "-fprofile-instr-generate=/data/misc/trace/clang-%p-%m.profraw"
@@ -247,9 +247,19 @@
 	return properties
 }
 
+type IsNativeCoverageNeededContext interface {
+	Config() android.Config
+	DeviceConfig() android.DeviceConfig
+	Device() bool
+}
+
+var _ IsNativeCoverageNeededContext = android.IncomingTransitionContext(nil)
+var _ IsNativeCoverageNeededContext = android.BaseModuleContext(nil)
+var _ IsNativeCoverageNeededContext = android.BottomUpMutatorContext(nil)
+
 type UseCoverage interface {
 	android.Module
-	IsNativeCoverageNeeded(ctx android.IncomingTransitionContext) bool
+	IsNativeCoverageNeeded(ctx IsNativeCoverageNeededContext) bool
 }
 
 // Coverage is an interface for non-CC modules to implement to be mutated for coverage
diff --git a/cc/library.go b/cc/library.go
index 4373b46..560b1ae 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -2220,8 +2220,12 @@
 		if variants[i] != "" || isLLNDK || isVendorPublicLibrary || isImportedApiLibrary {
 			// A stubs or LLNDK stubs variant.
 			c := m.(*Module)
-			c.sanitize = nil
-			c.stl = nil
+			if c.sanitize != nil {
+				c.sanitize.Properties.ForceDisable = true
+			}
+			if c.stl != nil {
+				c.stl.Properties.Stl = StringPtr("none")
+			}
 			c.Properties.PreventInstall = true
 			lib := moduleLibraryInterface(m)
 			isLatest := i == (len(versions) - 1)
diff --git a/cc/sanitize.go b/cc/sanitize.go
index d43a096..e6f00fa 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -383,7 +383,19 @@
 	Sanitize        SanitizeUserProps         `android:"arch_variant"`
 	SanitizeMutated sanitizeMutatedProperties `blueprint:"mutated"`
 
-	SanitizerEnabled  bool     `blueprint:"mutated"`
+	// ForceDisable is set by the version mutator to disable sanitization of stubs variants
+	ForceDisable bool `blueprint:"mutated"`
+
+	// SanitizerEnabled is set by begin() if any of the sanitize boolean properties are set after
+	// applying the logic that enables globally enabled sanitizers and disables any unsupported
+	// sanitizers.
+	// TODO(b/349906293): this has some unintuitive behavior.  It is set in begin() before the sanitize
+	//  mutator is run if any of the individual sanitizes  properties are set, and then the individual
+	//  sanitize properties are cleared in the non-sanitized variants, but this value is never cleared.
+	//  That results in SanitizerEnabled being set in variants that have no sanitizers enabled, causing
+	//  some of the sanitizer logic in flags() to be applied to the non-sanitized variant.
+	SanitizerEnabled bool `blueprint:"mutated"`
+
 	MinimalRuntimeDep bool     `blueprint:"mutated"`
 	BuiltinsDep       bool     `blueprint:"mutated"`
 	UbsanRuntimeDep   bool     `blueprint:"mutated"`
@@ -455,6 +467,10 @@
 	s := &sanitize.Properties.SanitizeMutated
 	s.copyUserPropertiesToMutated(&sanitize.Properties.Sanitize)
 
+	if sanitize.Properties.ForceDisable {
+		return
+	}
+
 	// Don't apply sanitizers to NDK code.
 	if ctx.useSdk() {
 		s.Never = BoolPtr(true)
@@ -765,6 +781,10 @@
 }
 
 func (s *sanitize) flags(ctx ModuleContext, flags Flags) Flags {
+	if s.Properties.ForceDisable {
+		return flags
+	}
+
 	if !s.Properties.SanitizerEnabled && !s.Properties.UbsanRuntimeDep {
 		return flags
 	}
@@ -1104,7 +1124,7 @@
 	if s == nil {
 		return false
 	}
-	if proptools.Bool(s.Properties.SanitizeMutated.Never) {
+	if s.Properties.ForceDisable || proptools.Bool(s.Properties.SanitizeMutated.Never) {
 		return false
 	}
 
@@ -1329,7 +1349,7 @@
 }
 
 func (c *Module) SanitizeNever() bool {
-	return Bool(c.sanitize.Properties.SanitizeMutated.Never)
+	return c.sanitize.Properties.ForceDisable || Bool(c.sanitize.Properties.SanitizeMutated.Never)
 }
 
 func (c *Module) IsSanitizerExplicitlyDisabled(t SanitizerType) bool {
@@ -1340,6 +1360,9 @@
 func sanitizerRuntimeDepsMutator(mctx android.TopDownMutatorContext) {
 	// Change this to PlatformSanitizable when/if non-cc modules support ubsan sanitizers.
 	if c, ok := mctx.Module().(*Module); ok && c.sanitize != nil {
+		if c.sanitize.Properties.ForceDisable {
+			return
+		}
 		isSanitizableDependencyTag := c.SanitizableDepTagChecker()
 		mctx.WalkDeps(func(child, parent android.Module) bool {
 			if !isSanitizableDependencyTag(mctx.OtherModuleDependencyTag(child)) {
@@ -1350,7 +1373,7 @@
 			if !ok || !d.static() {
 				return false
 			}
-			if d.sanitize != nil {
+			if d.sanitize != nil && !d.sanitize.Properties.ForceDisable {
 				if enableMinimalRuntime(d.sanitize) {
 					// If a static dependency is built with the minimal runtime,
 					// make sure we include the ubsan minimal runtime.
@@ -1385,6 +1408,10 @@
 		if !c.Enabled(mctx) {
 			return
 		}
+		if c.sanitize.Properties.ForceDisable {
+			return
+		}
+
 		var sanitizers []string
 		var diagSanitizers []string
 
diff --git a/cc/test.go b/cc/test.go
index 007932e..c9351e9 100644
--- a/cc/test.go
+++ b/cc/test.go
@@ -15,11 +15,9 @@
 package cc
 
 import (
+	"github.com/google/blueprint/proptools"
 	"path/filepath"
 	"strconv"
-	"strings"
-
-	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
 	"android/soong/tradefed"
@@ -75,13 +73,9 @@
 }
 
 type TestBinaryProperties struct {
-	// Create a separate binary for each source file.  Useful when there is
-	// global state that can not be torn down and reset between each test suite.
-	Test_per_src *bool
-
 	// Disables the creation of a test-specific directory when used with
 	// relative_install_path. Useful if several tests need to be in the same
-	// directory, but test_per_src doesn't work.
+	// directory.
 	No_named_install_directory *bool
 
 	// list of files or filegroup modules that provide data that should be installed alongside
@@ -174,86 +168,14 @@
 	return module.Init()
 }
 
-type testPerSrc interface {
-	testPerSrc() bool
-	srcs() []string
-	isAllTestsVariation() bool
-	setSrc(string, string)
-	unsetSrc()
-}
-
-func (test *testBinary) testPerSrc() bool {
-	return Bool(test.Properties.Test_per_src)
-}
-
-func (test *testBinary) srcs() []string {
-	return test.baseCompiler.Properties.Srcs
-}
-
 func (test *testBinary) dataPaths() []android.DataPath {
 	return test.data
 }
 
-func (test *testBinary) isAllTestsVariation() bool {
-	stem := test.binaryDecorator.Properties.Stem
-	return stem != nil && *stem == ""
-}
-
-func (test *testBinary) setSrc(name, src string) {
-	test.baseCompiler.Properties.Srcs = []string{src}
-	test.binaryDecorator.Properties.Stem = StringPtr(name)
-}
-
-func (test *testBinary) unsetSrc() {
-	test.baseCompiler.Properties.Srcs = nil
-	test.binaryDecorator.Properties.Stem = StringPtr("")
-}
-
 func (test *testBinary) testBinary() bool {
 	return true
 }
 
-var _ testPerSrc = (*testBinary)(nil)
-
-func TestPerSrcMutator(mctx android.BottomUpMutatorContext) {
-	if m, ok := mctx.Module().(*Module); ok {
-		if test, ok := m.linker.(testPerSrc); ok {
-			numTests := len(test.srcs())
-			if test.testPerSrc() && numTests > 0 {
-				if duplicate, found := android.CheckDuplicate(test.srcs()); found {
-					mctx.PropertyErrorf("srcs", "found a duplicate entry %q", duplicate)
-					return
-				}
-				testNames := make([]string, numTests)
-				for i, src := range test.srcs() {
-					testNames[i] = strings.TrimSuffix(filepath.Base(src), filepath.Ext(src))
-				}
-				// In addition to creating one variation per test source file,
-				// create an additional "all tests" variation named "", and have it
-				// depends on all other test_per_src variations. This is useful to
-				// create subsequent dependencies of a given module on all
-				// test_per_src variations created above: by depending on
-				// variation "", that module will transitively depend on all the
-				// other test_per_src variations without the need to know their
-				// name or even their number.
-				testNames = append(testNames, "")
-				tests := mctx.CreateLocalVariations(testNames...)
-				allTests := tests[numTests]
-				allTests.(*Module).linker.(testPerSrc).unsetSrc()
-				// Prevent the "all tests" variation from being installable nor
-				// exporting to Make, as it won't create any output file.
-				allTests.(*Module).Properties.PreventInstall = true
-				allTests.(*Module).Properties.HideFromMake = true
-				for i, src := range test.srcs() {
-					tests[i].(*Module).linker.(testPerSrc).setSrc(testNames[i], src)
-					mctx.AddInterVariantDependency(testPerSrcDepTag, allTests, tests[i])
-				}
-				mctx.AliasVariation("")
-			}
-		}
-	}
-}
-
 type testDecorator struct {
 	LinkerProperties    TestLinkerProperties
 	InstallerProperties TestInstallerProperties
@@ -385,10 +307,6 @@
 	}
 	moduleInfoJSON.TestConfig = append(moduleInfoJSON.TestConfig, test.extraTestConfigs.Strings()...)
 
-	if Bool(test.Properties.Test_per_src) {
-		moduleInfoJSON.SubName = "_" + String(test.binaryDecorator.Properties.Stem)
-	}
-
 	moduleInfoJSON.DataDependencies = append(moduleInfoJSON.DataDependencies, test.Properties.Data_bins...)
 
 	if len(test.InstallerProperties.Test_suites) > 0 {
diff --git a/filesystem/filesystem.go b/filesystem/filesystem.go
index ffa30a0..5add954 100644
--- a/filesystem/filesystem.go
+++ b/filesystem/filesystem.go
@@ -632,7 +632,7 @@
 
 var _ cc.UseCoverage = (*filesystem)(nil)
 
-func (*filesystem) IsNativeCoverageNeeded(ctx android.IncomingTransitionContext) bool {
+func (*filesystem) IsNativeCoverageNeeded(ctx cc.IsNativeCoverageNeededContext) bool {
 	return ctx.Device() && ctx.DeviceConfig().NativeCoverageEnabled()
 }
 
diff --git a/genrule/genrule.go b/genrule/genrule.go
index c0942d3..5b40768 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -754,6 +754,7 @@
 func GenSrcsFactory() android.Module {
 	m := NewGenSrcs()
 	android.InitAndroidModule(m)
+	android.InitDefaultableModule(m)
 	return m
 }
 
diff --git a/java/aar.go b/java/aar.go
index 4aae62a..2f49a95 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -798,18 +798,6 @@
 	aarFile android.WritablePath
 }
 
-var _ android.OutputFileProducer = (*AndroidLibrary)(nil)
-
-// For OutputFileProducer interface
-func (a *AndroidLibrary) OutputFiles(tag string) (android.Paths, error) {
-	switch tag {
-	case ".aar":
-		return []android.Path{a.aarFile}, nil
-	default:
-		return a.Library.OutputFiles(tag)
-	}
-}
-
 var _ AndroidLibraryDependency = (*AndroidLibrary)(nil)
 
 func (a *AndroidLibrary) DepsMutator(ctx android.BottomUpMutatorContext) {
@@ -911,6 +899,13 @@
 	android.SetProvider(ctx, FlagsPackagesProvider, FlagsPackages{
 		AconfigTextFiles: aconfigTextFilePaths,
 	})
+
+	a.setOutputFiles(ctx)
+}
+
+func (a *AndroidLibrary) setOutputFiles(ctx android.ModuleContext) {
+	ctx.SetOutputFiles([]android.Path{a.aarFile}, ".aar")
+	setOutputFiles(ctx, a.Library.Module)
 }
 
 func (a *AndroidLibrary) IDEInfo(dpInfo *android.IdeInfo) {
diff --git a/java/aar_test.go b/java/aar_test.go
index 1ab1f9c..ebad310 100644
--- a/java/aar_test.go
+++ b/java/aar_test.go
@@ -159,19 +159,19 @@
 	bar := result.ModuleForTests("bar", "android_common")
 	baz := result.ModuleForTests("baz", "android_common")
 
-	fooOutputPath := android.OutputFileForModule(android.PathContext(nil), foo.Module(), "")
+	fooOutputPaths := foo.OutputFiles(t, "")
 	barOutputPaths := bar.OutputFiles(t, "")
 	bazOutputPaths := baz.OutputFiles(t, "")
 
-	android.AssertPathRelativeToTopEquals(t, "foo output path",
-		"out/soong/.intermediates/foo/android_common/withres/foo.jar", fooOutputPath)
+	android.AssertPathsRelativeToTopEquals(t, "foo output path",
+		[]string{"out/soong/.intermediates/foo/android_common/withres/foo.jar"}, fooOutputPaths)
 	android.AssertPathsRelativeToTopEquals(t, "bar output path",
 		[]string{"out/soong/.intermediates/bar/android_common/aar/bar.jar"}, barOutputPaths)
 	android.AssertPathsRelativeToTopEquals(t, "baz output path",
 		[]string{"out/soong/.intermediates/baz/android_common/withres/baz.jar"}, bazOutputPaths)
 
 	android.AssertStringEquals(t, "foo relative output path",
-		"foo.jar", fooOutputPath.Rel())
+		"foo.jar", fooOutputPaths[0].Rel())
 	android.AssertStringEquals(t, "bar relative output path",
 		"bar.jar", barOutputPaths[0].Rel())
 	android.AssertStringEquals(t, "baz relative output path",
diff --git a/java/app.go b/java/app.go
index e8c9bfe..19dc8d5 100644
--- a/java/app.go
+++ b/java/app.go
@@ -1017,6 +1017,22 @@
 			isPrebuilt:     false,
 		},
 	)
+
+	a.setOutputFiles(ctx)
+}
+
+func (a *AndroidApp) setOutputFiles(ctx android.ModuleContext) {
+	ctx.SetOutputFiles([]android.Path{a.proguardOptionsFile}, ".aapt.proguardOptionsFile")
+	if a.aaptSrcJar != nil {
+		ctx.SetOutputFiles([]android.Path{a.aaptSrcJar}, ".aapt.srcjar")
+	}
+	if a.rJar != nil {
+		ctx.SetOutputFiles([]android.Path{a.rJar}, ".aapt.jar")
+	}
+	ctx.SetOutputFiles([]android.Path{a.outputFile}, ".apk")
+	ctx.SetOutputFiles([]android.Path{a.exportPackage}, ".export-package.apk")
+	ctx.SetOutputFiles([]android.Path{a.aapt.manifestPath}, ".manifest.xml")
+	setOutputFiles(ctx, a.Library.Module)
 }
 
 type appDepsInterface interface {
@@ -1207,36 +1223,11 @@
 	return a.Library.DepIsInSameApex(ctx, dep)
 }
 
-// For OutputFileProducer interface
-func (a *AndroidApp) OutputFiles(tag string) (android.Paths, error) {
-	switch tag {
-	// In some instances, it can be useful to reference the aapt-generated flags from another
-	// target, e.g., system server implements services declared in the framework-res manifest.
-	case ".aapt.proguardOptionsFile":
-		return []android.Path{a.proguardOptionsFile}, nil
-	case ".aapt.srcjar":
-		if a.aaptSrcJar != nil {
-			return []android.Path{a.aaptSrcJar}, nil
-		}
-	case ".aapt.jar":
-		if a.rJar != nil {
-			return []android.Path{a.rJar}, nil
-		}
-	case ".apk":
-		return []android.Path{a.outputFile}, nil
-	case ".export-package.apk":
-		return []android.Path{a.exportPackage}, nil
-	case ".manifest.xml":
-		return []android.Path{a.aapt.manifestPath}, nil
-	}
-	return a.Library.OutputFiles(tag)
-}
-
 func (a *AndroidApp) Privileged() bool {
 	return Bool(a.appProperties.Privileged)
 }
 
-func (a *AndroidApp) IsNativeCoverageNeeded(ctx android.IncomingTransitionContext) bool {
+func (a *AndroidApp) IsNativeCoverageNeeded(ctx cc.IsNativeCoverageNeededContext) bool {
 	return ctx.Device() && ctx.DeviceConfig().NativeCoverageEnabled()
 }
 
diff --git a/java/app_test.go b/java/app_test.go
index 5770ab4..e878ccf 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -119,10 +119,7 @@
 		foo.Output(expectedOutput)
 	}
 
-	outputFiles, err := foo.Module().(*AndroidApp).OutputFiles("")
-	if err != nil {
-		t.Fatal(err)
-	}
+	outputFiles := foo.OutputFiles(t, "")
 	android.AssertPathsRelativeToTopEquals(t, `OutputFiles("")`, expectedOutputs, outputFiles)
 }
 
diff --git a/java/base.go b/java/base.go
index ee8df3e..fc68d20 100644
--- a/java/base.go
+++ b/java/base.go
@@ -652,35 +652,21 @@
 	android.SetProvider(ctx, hiddenAPIPropertyInfoProvider, hiddenAPIInfo)
 }
 
-func (j *Module) OutputFiles(tag string) (android.Paths, error) {
-	switch tag {
-	case "":
-		return append(android.Paths{j.outputFile}, j.extraOutputFiles...), nil
-	case android.DefaultDistTag:
-		return android.Paths{j.outputFile}, nil
-	case ".jar":
-		return android.Paths{j.implementationAndResourcesJar}, nil
-	case ".hjar":
-		return android.Paths{j.headerJarFile}, nil
-	case ".proguard_map":
-		if j.dexer.proguardDictionary.Valid() {
-			return android.Paths{j.dexer.proguardDictionary.Path()}, nil
-		}
-		return nil, fmt.Errorf("%q was requested, but no output file was found.", tag)
-	case ".generated_srcjars":
-		return j.properties.Generated_srcjars, nil
-	case ".lint":
-		if j.linter.outputs.xml != nil {
-			return android.Paths{j.linter.outputs.xml}, nil
-		}
-		return nil, fmt.Errorf("%q was requested, but no output file was found.", tag)
-	default:
-		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
+// helper method for java modules to set OutputFilesProvider
+func setOutputFiles(ctx android.ModuleContext, m Module) {
+	ctx.SetOutputFiles(append(android.Paths{m.outputFile}, m.extraOutputFiles...), "")
+	ctx.SetOutputFiles(android.Paths{m.outputFile}, android.DefaultDistTag)
+	ctx.SetOutputFiles(android.Paths{m.implementationAndResourcesJar}, ".jar")
+	ctx.SetOutputFiles(android.Paths{m.headerJarFile}, ".hjar")
+	if m.dexer.proguardDictionary.Valid() {
+		ctx.SetOutputFiles(android.Paths{m.dexer.proguardDictionary.Path()}, ".proguard_map")
+	}
+	ctx.SetOutputFiles(m.properties.Generated_srcjars, ".generated_srcjars")
+	if m.linter.outputs.xml != nil {
+		ctx.SetOutputFiles(android.Paths{m.linter.outputs.xml}, ".lint")
 	}
 }
 
-var _ android.OutputFileProducer = (*Module)(nil)
-
 func InitJavaModule(module android.DefaultableModule, hod android.HostOrDeviceSupported) {
 	initJavaModule(module, hod, false)
 }
diff --git a/java/dex.go b/java/dex.go
index c75e774..7bb6925 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -91,6 +91,10 @@
 
 	// Exclude kotlinc generate files: *.kotlin_module, *.kotlin_builtins. Defaults to false.
 	Exclude_kotlinc_generated_files *bool
+
+	// Disable dex container (also known as "multi-dex").
+	// This may be necessary as a temporary workaround to mask toolchain bugs (see b/341652226).
+	No_dex_container *bool
 }
 
 type dexer struct {
diff --git a/java/java.go b/java/java.go
index e4abbcd..88b31b5 100644
--- a/java/java.go
+++ b/java/java.go
@@ -977,6 +977,8 @@
 		TestOnly:       Bool(j.sourceProperties.Test_only),
 		TopLevelTarget: j.sourceProperties.Top_level_test_target,
 	})
+
+	setOutputFiles(ctx, j.Module)
 }
 
 func (j *Library) setInstallRules(ctx android.ModuleContext, installModuleName string) {
@@ -1356,7 +1358,7 @@
 	return true
 }
 
-func (j *TestHost) IsNativeCoverageNeeded(ctx android.IncomingTransitionContext) bool {
+func (j *TestHost) IsNativeCoverageNeeded(ctx cc.IsNativeCoverageNeededContext) bool {
 	return ctx.DeviceConfig().NativeCoverageEnabled()
 }
 
@@ -1836,6 +1838,8 @@
 		// libraries.  This is verified by TestBinary.
 		j.binaryFile = ctx.InstallExecutable(android.PathForModuleInstall(ctx, "bin"),
 			ctx.ModuleName()+ext, j.wrapperFile)
+
+		setOutputFiles(ctx, j.Library.Module)
 	}
 }
 
diff --git a/java/java_test.go b/java/java_test.go
index 51c969e..33079f3 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -2993,19 +2993,19 @@
 	bar := result.ModuleForTests("bar", "android_common")
 	baz := result.ModuleForTests("baz", "android_common")
 
-	fooOutputPath := android.OutputFileForModule(android.PathContext(nil), foo.Module(), "")
+	fooOutputPaths := foo.OutputFiles(t, "")
 	barOutputPaths := bar.OutputFiles(t, "")
 	bazOutputPaths := baz.OutputFiles(t, "")
 
-	android.AssertPathRelativeToTopEquals(t, "foo output path",
-		"out/soong/.intermediates/foo/android_common/javac/foo.jar", fooOutputPath)
+	android.AssertPathsRelativeToTopEquals(t, "foo output path",
+		[]string{"out/soong/.intermediates/foo/android_common/javac/foo.jar"}, fooOutputPaths)
 	android.AssertPathsRelativeToTopEquals(t, "bar output path",
 		[]string{"out/soong/.intermediates/bar/android_common/combined/bar.jar"}, barOutputPaths)
 	android.AssertPathsRelativeToTopEquals(t, "baz output path",
 		[]string{"out/soong/.intermediates/baz/android_common/combined/baz.jar"}, bazOutputPaths)
 
 	android.AssertStringEquals(t, "foo relative output path",
-		"foo.jar", fooOutputPath.Rel())
+		"foo.jar", fooOutputPaths[0].Rel())
 	android.AssertStringEquals(t, "bar relative output path",
 		"bar.jar", barOutputPaths[0].Rel())
 	android.AssertStringEquals(t, "baz relative output path",
diff --git a/java/sdk_library.go b/java/sdk_library.go
index e6cb6c4..1eb7ab8 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -538,9 +538,6 @@
 	// of the API.
 	Api_packages []string
 
-	// list of package names that must be hidden from the API
-	Hidden_api_packages []string
-
 	// the relative path to the directory containing the api specification files.
 	// Defaults to "api".
 	Api_dir *string
@@ -1086,57 +1083,26 @@
 	return regexp.MustCompile(fmt.Sprintf(`^\.(%s)\.(%s)$`, scopesRegexp, componentsRegexp))
 }()
 
-// For OutputFileProducer interface
-//
-// .<scope>.<component name>, for all ComponentNames (for example: .public.removed-api.txt)
-func (c *commonToSdkLibraryAndImport) commonOutputFiles(tag string) (android.Paths, error) {
-	if groups := tagSplitter.FindStringSubmatch(tag); groups != nil {
-		scopeName := groups[1]
-		component := groups[2]
-
-		if scope, ok := scopeByName[scopeName]; ok {
-			paths := c.findScopePaths(scope)
-			if paths == nil {
-				return nil, fmt.Errorf("%q does not provide api scope %s", c.module.RootLibraryName(), scopeName)
-			}
-
-			switch component {
-			case stubsSourceComponentName:
-				if paths.stubsSrcJar.Valid() {
-					return android.Paths{paths.stubsSrcJar.Path()}, nil
-				}
-
-			case apiTxtComponentName:
-				if paths.currentApiFilePath.Valid() {
-					return android.Paths{paths.currentApiFilePath.Path()}, nil
-				}
-
-			case removedApiTxtComponentName:
-				if paths.removedApiFilePath.Valid() {
-					return android.Paths{paths.removedApiFilePath.Path()}, nil
-				}
-
-			case annotationsComponentName:
-				if paths.annotationsZip.Valid() {
-					return android.Paths{paths.annotationsZip.Path()}, nil
-				}
-			}
-
-			return nil, fmt.Errorf("%s not available for api scope %s", component, scopeName)
-		} else {
-			return nil, fmt.Errorf("unknown scope %s in %s", scope, tag)
+func (module *commonToSdkLibraryAndImport) setOutputFiles(ctx android.ModuleContext) {
+	if module.doctagPaths != nil {
+		ctx.SetOutputFiles(module.doctagPaths, ".doctags")
+	}
+	for _, scopeName := range android.SortedKeys(scopeByName) {
+		paths := module.findScopePaths(scopeByName[scopeName])
+		if paths == nil {
+			continue
 		}
-
-	} else {
-		switch tag {
-		case ".doctags":
-			if c.doctagPaths != nil {
-				return c.doctagPaths, nil
-			} else {
-				return nil, fmt.Errorf("no doctag_files specified on %s", c.module.RootLibraryName())
+		componentToOutput := map[string]android.OptionalPath{
+			stubsSourceComponentName:   paths.stubsSrcJar,
+			apiTxtComponentName:        paths.currentApiFilePath,
+			removedApiTxtComponentName: paths.removedApiFilePath,
+			annotationsComponentName:   paths.annotationsZip,
+		}
+		for _, component := range android.SortedKeys(componentToOutput) {
+			if componentToOutput[component].Valid() {
+				ctx.SetOutputFiles(android.Paths{componentToOutput[component].Path()}, "."+scopeName+"."+component)
 			}
 		}
-		return nil, nil
 	}
 }
 
@@ -1579,20 +1545,6 @@
 	}
 }
 
-func (module *SdkLibrary) OutputFiles(tag string) (android.Paths, error) {
-	paths, err := module.commonOutputFiles(tag)
-	if paths != nil || err != nil {
-		return paths, err
-	}
-	if module.requiresRuntimeImplementationLibrary() {
-		return module.implLibraryModule.OutputFiles(tag)
-	}
-	if tag == "" {
-		return nil, nil
-	}
-	return nil, fmt.Errorf("unsupported module reference tag %q", tag)
-}
-
 func (module *SdkLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	if disableSourceApexVariant(ctx) {
 		// Prebuilts are active, do not create the installation rules for the source javalib.
@@ -1702,6 +1654,10 @@
 		}
 	}
 	android.SetProvider(ctx, android.AdditionalSdkInfoProvider, android.AdditionalSdkInfo{additionalSdkInfo})
+	module.setOutputFiles(ctx)
+	if module.requiresRuntimeImplementationLibrary() && module.implLibraryModule != nil {
+		setOutputFiles(ctx, module.implLibraryModule.Module)
+	}
 }
 
 func (module *SdkLibrary) BuiltInstalledForApex() []dexpreopterInstall {
@@ -2007,10 +1963,6 @@
 	if len(module.sdkLibraryProperties.Api_packages) != 0 {
 		droidstubsArgs = append(droidstubsArgs, "--stub-packages "+strings.Join(module.sdkLibraryProperties.Api_packages, ":"))
 	}
-	if len(module.sdkLibraryProperties.Hidden_api_packages) != 0 {
-		droidstubsArgs = append(droidstubsArgs,
-			android.JoinWithPrefix(module.sdkLibraryProperties.Hidden_api_packages, " --hide-package "))
-	}
 	droidstubsArgs = append(droidstubsArgs, module.sdkLibraryProperties.Droiddoc_options...)
 	disabledWarnings := []string{"HiddenSuperclass"}
 	if proptools.BoolDefault(module.sdkLibraryProperties.Api_lint.Legacy_errors_allowed, true) {
@@ -2937,18 +2889,6 @@
 
 var _ hiddenAPIModule = (*SdkLibraryImport)(nil)
 
-func (module *SdkLibraryImport) OutputFiles(tag string) (android.Paths, error) {
-	paths, err := module.commonOutputFiles(tag)
-	if paths != nil || err != nil {
-		return paths, err
-	}
-	if module.implLibraryModule != nil {
-		return module.implLibraryModule.OutputFiles(tag)
-	} else {
-		return nil, nil
-	}
-}
-
 func (module *SdkLibraryImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	module.generateCommonBuildActions(ctx)
 
@@ -3031,6 +2971,11 @@
 			}
 		}
 	}
+
+	module.setOutputFiles(ctx)
+	if module.implLibraryModule != nil {
+		setOutputFiles(ctx, module.implLibraryModule.Module)
+	}
 }
 
 func (module *SdkLibraryImport) sdkJars(ctx android.BaseModuleContext, sdkVersion android.SdkSpec, headerJars bool) android.Paths {
diff --git a/java/sdk_library_test.go b/java/sdk_library_test.go
index 39f8c76..a8a1494 100644
--- a/java/sdk_library_test.go
+++ b/java/sdk_library_test.go
@@ -492,7 +492,7 @@
 		PrepareForTestWithJavaSdkLibraryFiles,
 		FixtureWithLastReleaseApis("foo"),
 	).
-		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module "bar" variant "android_common": failed to get output file from module "foo" at tag ".public.annotations.zip": annotations.zip not available for api scope public`)).
+		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module "bar" variant "android_common": unsupported module reference tag ".public.annotations.zip"`)).
 		RunTestWithBp(t, `
 		java_sdk_library {
 			name: "foo",
@@ -517,7 +517,7 @@
 		PrepareForTestWithJavaSdkLibraryFiles,
 		FixtureWithLastReleaseApis("foo"),
 	).
-		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`"foo" does not provide api scope system`)).
+		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module "bar" variant "android_common": unsupported module reference tag ".system.stubs.source"`)).
 		RunTestWithBp(t, `
 		java_sdk_library {
 			name: "foo",
@@ -606,7 +606,7 @@
 
 	t.Run("stubs.source", func(t *testing.T) {
 		prepareForJavaTest.
-			ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`stubs.source not available for api scope public`)).
+			ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module "foo" is not a SourceFileProducer or having valid output file for tag ".public.stubs.source"`)).
 			RunTestWithBp(t, bp+`
 				java_library {
 					name: "bar",
@@ -621,7 +621,7 @@
 
 	t.Run("api.txt", func(t *testing.T) {
 		prepareForJavaTest.
-			ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`api.txt not available for api scope public`)).
+			ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module "foo" is not a SourceFileProducer or having valid output file for tag ".public.api.txt"`)).
 			RunTestWithBp(t, bp+`
 				java_library {
 					name: "bar",
@@ -635,7 +635,7 @@
 
 	t.Run("removed-api.txt", func(t *testing.T) {
 		prepareForJavaTest.
-			ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`removed-api.txt not available for api scope public`)).
+			ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module "foo" is not a SourceFileProducer or having valid output file for tag ".public.removed-api.txt"`)).
 			RunTestWithBp(t, bp+`
 				java_library {
 					name: "bar",
diff --git a/rust/benchmark.go b/rust/benchmark.go
index c0f1e24..8c3e515 100644
--- a/rust/benchmark.go
+++ b/rust/benchmark.go
@@ -22,7 +22,7 @@
 type BenchmarkProperties struct {
 	// Disables the creation of a test-specific directory when used with
 	// relative_install_path. Useful if several tests need to be in the same
-	// directory, but test_per_src doesn't work.
+	// directory.
 	No_named_install_directory *bool
 
 	// the name of the test configuration (for example "AndroidBenchmark.xml") that should be
diff --git a/rust/builder.go b/rust/builder.go
index 1ce92f4..f469f56 100644
--- a/rust/builder.go
+++ b/rust/builder.go
@@ -520,6 +520,9 @@
 	// this flag.
 	rustdocFlags = append(rustdocFlags, "-Z", "unstable-options", "--enable-index-page")
 
+	// Ensure we use any special-case code-paths for Soong.
+	rustdocFlags = append(rustdocFlags, "--cfg", "soong")
+
 	targetTriple := ctx.toolchain().RustTriple()
 
 	// Collect rustc flags
diff --git a/rust/library.go b/rust/library.go
index 96c02c8..ba73f27 100644
--- a/rust/library.go
+++ b/rust/library.go
@@ -86,8 +86,6 @@
 	VariantIsRlib bool `blueprint:"mutated"`
 	// This variant is a shared library
 	VariantIsShared bool `blueprint:"mutated"`
-	// This variant is a static library
-	VariantIsStatic bool `blueprint:"mutated"`
 	// This variant is a source provider
 	VariantIsSource bool `blueprint:"mutated"`
 
@@ -179,7 +177,7 @@
 }
 
 func (library *libraryDecorator) static() bool {
-	return library.MutatedProperties.VariantIsStatic
+	return false
 }
 
 func (library *libraryDecorator) source() bool {
@@ -205,14 +203,12 @@
 func (library *libraryDecorator) setRlib() {
 	library.MutatedProperties.VariantIsRlib = true
 	library.MutatedProperties.VariantIsDylib = false
-	library.MutatedProperties.VariantIsStatic = false
 	library.MutatedProperties.VariantIsShared = false
 }
 
 func (library *libraryDecorator) setDylib() {
 	library.MutatedProperties.VariantIsRlib = false
 	library.MutatedProperties.VariantIsDylib = true
-	library.MutatedProperties.VariantIsStatic = false
 	library.MutatedProperties.VariantIsShared = false
 }
 
@@ -229,17 +225,13 @@
 }
 
 func (library *libraryDecorator) setShared() {
-	library.MutatedProperties.VariantIsStatic = false
 	library.MutatedProperties.VariantIsShared = true
 	library.MutatedProperties.VariantIsRlib = false
 	library.MutatedProperties.VariantIsDylib = false
 }
 
 func (library *libraryDecorator) setStatic() {
-	library.MutatedProperties.VariantIsStatic = true
-	library.MutatedProperties.VariantIsShared = false
-	library.MutatedProperties.VariantIsRlib = false
-	library.MutatedProperties.VariantIsDylib = false
+	panic(fmt.Errorf("static variant is not supported for rust modules, use the rlib variant instead"))
 }
 
 func (library *libraryDecorator) setSource() {
diff --git a/rust/rust.go b/rust/rust.go
index 6b7b2fb..9dae75e 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -501,7 +501,7 @@
 
 var _ cc.Coverage = (*Module)(nil)
 
-func (mod *Module) IsNativeCoverageNeeded(ctx android.IncomingTransitionContext) bool {
+func (mod *Module) IsNativeCoverageNeeded(ctx cc.IsNativeCoverageNeededContext) bool {
 	return mod.coverage != nil && mod.coverage.Properties.NeedCoverageVariant
 }
 
@@ -1107,7 +1107,6 @@
 	rlibDepTag          = dependencyTag{name: "rlibTag", library: true}
 	dylibDepTag         = dependencyTag{name: "dylib", library: true, dynamic: true}
 	procMacroDepTag     = dependencyTag{name: "procMacro", procMacro: true}
-	testPerSrcDepTag    = dependencyTag{name: "rust_unit_tests"}
 	sourceDepTag        = dependencyTag{name: "source"}
 	dataLibDepTag       = dependencyTag{name: "data lib"}
 	dataBinDepTag       = dependencyTag{name: "data bin"}
diff --git a/rust/test.go b/rust/test.go
index 2583893..3087d8d 100644
--- a/rust/test.go
+++ b/rust/test.go
@@ -27,7 +27,7 @@
 type TestProperties struct {
 	// Disables the creation of a test-specific directory when used with
 	// relative_install_path. Useful if several tests need to be in the same
-	// directory, but test_per_src doesn't work.
+	// directory.
 	No_named_install_directory *bool
 
 	// the name of the test configuration (for example "AndroidTest.xml") that should be
diff --git a/sdk/cc_sdk_test.go b/sdk/cc_sdk_test.go
index c9e20b3..5d76930 100644
--- a/sdk/cc_sdk_test.go
+++ b/sdk/cc_sdk_test.go
@@ -2196,6 +2196,7 @@
     prefer: false,
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:platform"],
+    stl: "none",
     compile_multilib: "both",
     stubs: {
         versions: [
diff --git a/ui/build/config.go b/ui/build/config.go
index 52c5e23..2470f84 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -121,6 +121,10 @@
 	// There's quite a bit of overlap with module-info.json and soong module graph. We
 	// could consider merging them.
 	moduleDebugFile string
+
+	// Whether to use n2 instead of ninja.  This is controlled with the
+	// environment variable SOONG_USE_N2
+	useN2 bool
 }
 
 type NinjaWeightListSource uint
@@ -283,6 +287,10 @@
 		ret.moduleDebugFile, _ = filepath.Abs(shared.JoinPath(ret.SoongOutDir(), "soong-debug-info.json"))
 	}
 
+	if os.Getenv("SOONG_USE_N2") == "true" {
+		ret.useN2 = true
+	}
+
 	ret.environ.Unset(
 		// We're already using it
 		"USE_SOONG_UI",
@@ -339,6 +347,9 @@
 
 		// We read it here already, don't let others share in the fun
 		"GENERATE_SOONG_DEBUG",
+
+		// Use config.useN2 instead.
+		"SOONG_USE_N2",
 	)
 
 	if ret.UseGoma() || ret.ForceUseGoma() {
diff --git a/ui/build/ninja.go b/ui/build/ninja.go
index ae27330..1935e72 100644
--- a/ui/build/ninja.go
+++ b/ui/build/ninja.go
@@ -56,6 +56,17 @@
 		"-d", "stats",
 		"--frontend_file", fifo,
 	}
+	if config.useN2 {
+		executable = config.PrebuiltBuildTool("n2")
+		args = []string{
+			"-d", "trace",
+			// TODO: implement these features, or remove them.
+			//"-d", "keepdepfile",
+			//"-d", "keeprsp",
+			//"-d", "stats",
+			"--frontend-file", fifo,
+		}
+	}
 
 	args = append(args, config.NinjaArgs()...)
 
@@ -72,17 +83,21 @@
 
 	args = append(args, "-f", config.CombinedNinjaFile())
 
-	args = append(args,
-		"-o", "usesphonyoutputs=yes",
-		"-w", "dupbuild=err",
-		"-w", "missingdepfile=err")
+	if !config.useN2 {
+		args = append(args,
+			"-o", "usesphonyoutputs=yes",
+			"-w", "dupbuild=err",
+			"-w", "missingdepfile=err")
+	}
 
 	if !config.BuildBrokenMissingOutputs() {
 		// Missing outputs will be treated as errors.
 		// BUILD_BROKEN_MISSING_OUTPUTS can be used to bypass this check.
-		args = append(args,
-			"-w", "missingoutfile=err",
-		)
+		if !config.useN2 {
+			args = append(args,
+				"-w", "missingoutfile=err",
+			)
+		}
 	}
 
 	cmd := Command(ctx, config, "ninja", executable, args...)
@@ -97,16 +112,22 @@
 
 	switch config.NinjaWeightListSource() {
 	case NINJA_LOG:
-		cmd.Args = append(cmd.Args, "-o", "usesninjalogasweightlist=yes")
+		if !config.useN2 {
+			cmd.Args = append(cmd.Args, "-o", "usesninjalogasweightlist=yes")
+		}
 	case EVENLY_DISTRIBUTED:
 		// pass empty weight list means ninja considers every tasks's weight as 1(default value).
-		cmd.Args = append(cmd.Args, "-o", "usesweightlist=/dev/null")
+		if !config.useN2 {
+			cmd.Args = append(cmd.Args, "-o", "usesweightlist=/dev/null")
+		}
 	case EXTERNAL_FILE:
 		fallthrough
 	case HINT_FROM_SOONG:
 		// The weight list is already copied/generated.
-		ninjaWeightListPath := filepath.Join(config.OutDir(), ninjaWeightListFileName)
-		cmd.Args = append(cmd.Args, "-o", "usesweightlist="+ninjaWeightListPath)
+		if !config.useN2 {
+			ninjaWeightListPath := filepath.Join(config.OutDir(), ninjaWeightListFileName)
+			cmd.Args = append(cmd.Args, "-o", "usesweightlist="+ninjaWeightListPath)
+		}
 	}
 
 	// Allow both NINJA_ARGS and NINJA_EXTRA_ARGS, since both have been
@@ -206,11 +227,16 @@
 			// We don't want this build broken flag to cause reanalysis, so allow it through to the
 			// actions.
 			"BUILD_BROKEN_INCORRECT_PARTITION_IMAGES",
+			"SOONG_USE_N2",
+			"RUST_BACKTRACE",
 		}, config.BuildBrokenNinjaUsesEnvVars()...)...)
 	}
 
 	cmd.Environment.Set("DIST_DIR", config.DistDir())
 	cmd.Environment.Set("SHELL", "/bin/bash")
+	if config.useN2 {
+		cmd.Environment.Set("RUST_BACKTRACE", "1")
+	}
 
 	// Print the environment variables that Ninja is operating in.
 	ctx.Verboseln("Ninja environment: ")
diff --git a/ui/build/soong.go b/ui/build/soong.go
index 77fee0a..e18cc25 100644
--- a/ui/build/soong.go
+++ b/ui/build/soong.go
@@ -638,6 +638,22 @@
 			"--frontend_file", fifo,
 			"-f", filepath.Join(config.SoongOutDir(), "bootstrap.ninja"),
 		}
+		if config.useN2 {
+			ninjaArgs = []string{
+				// TODO: implement these features, or remove them.
+				//"-d", "keepdepfile",
+				//"-d", "stats",
+				//"-o", "usesphonyoutputs=yes",
+				//"-o", "preremoveoutputs=yes",
+				//"-w", "dupbuild=err",
+				//"-w", "outputdir=err",
+				//"-w", "missingoutfile=err",
+				"-v",
+				"-j", strconv.Itoa(config.Parallel()),
+				"--frontend-file", fifo,
+				"-f", filepath.Join(config.SoongOutDir(), "bootstrap.ninja"),
+			}
+		}
 
 		if extra, ok := config.Environment().Get("SOONG_UI_NINJA_ARGS"); ok {
 			ctx.Printf(`CAUTION: arguments in $SOONG_UI_NINJA_ARGS=%q, e.g. "-n", can make soong_build FAIL or INCORRECT`, extra)
@@ -645,8 +661,13 @@
 		}
 
 		ninjaArgs = append(ninjaArgs, targets...)
+		ninjaCmd := config.PrebuiltBuildTool("ninja")
+		if config.useN2 {
+			ninjaCmd = config.PrebuiltBuildTool("n2")
+		}
+
 		cmd := Command(ctx, config, "soong bootstrap",
-			config.PrebuiltBuildTool("ninja"), ninjaArgs...)
+			ninjaCmd, ninjaArgs...)
 
 		var ninjaEnv Environment