Merge "cc: export LOCAL_ACONFIG_FILES for printflags" into main
diff --git a/android/config.go b/android/config.go
index a9fcca5..9293505 100644
--- a/android/config.go
+++ b/android/config.go
@@ -195,10 +195,12 @@
 	return String(c.config.productVariables.DeviceMaxPageSizeSupported)
 }
 
-// PageSizeAgnostic returns true when AOSP is page size agnostic,
-// othersise it returns false.
-func (c Config) PageSizeAgnostic() bool {
-	return Bool(c.config.productVariables.DevicePageSizeAgnostic)
+// NoBionicPageSizeMacro returns true when AOSP is page size agnostic.
+// This means that the bionic's macro PAGE_SIZE won't be defined.
+// Returns false when AOSP is NOT page size agnostic.
+// This means that bionic's macro PAGE_SIZE is defined.
+func (c Config) NoBionicPageSizeMacro() bool {
+	return Bool(c.config.productVariables.DeviceNoBionicPageSizeMacro)
 }
 
 // The release version passed to aconfig, derived from RELEASE_VERSION
@@ -239,6 +241,11 @@
 		Bool(c.config.productVariables.ReleaseDefaultModuleBuildFromSource)
 }
 
+// Enables ABI monitoring of NDK libraries
+func (c Config) ReleaseNdkAbiMonitored() bool {
+	return c.config.productVariables.GetBuildFlagBool("RELEASE_NDK_ABI_MONITORED")
+}
+
 // A DeviceConfig object represents the configuration for a particular device
 // being built. For now there will only be one of these, but in the future there
 // may be multiple devices being built.
diff --git a/android/filegroup.go b/android/filegroup.go
index 856c50e..c34a615 100644
--- a/android/filegroup.go
+++ b/android/filegroup.go
@@ -290,6 +290,7 @@
 	if fg.properties.Path != nil {
 		fg.srcs = PathsWithModuleSrcSubDir(ctx, fg.srcs, String(fg.properties.Path))
 	}
+	ctx.SetProvider(blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: fg.srcs.Strings()})
 }
 
 func (fg *fileGroup) Srcs() Paths {
diff --git a/android/variable.go b/android/variable.go
index f4c6e1c..aecad3b 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -224,7 +224,7 @@
 	DeviceCurrentApiLevelForVendorModules *string  `json:",omitempty"`
 	DeviceSystemSdkVersions               []string `json:",omitempty"`
 	DeviceMaxPageSizeSupported            *string  `json:",omitempty"`
-	DevicePageSizeAgnostic                *bool    `json:",omitempty"`
+	DeviceNoBionicPageSizeMacro           *bool    `json:",omitempty"`
 
 	VendorApiLevel *string `json:",omitempty"`
 
@@ -582,20 +582,20 @@
 		Platform_version_all_preview_codenames: []string{"S"},
 		Platform_vndk_version:                  stringPtr("S"),
 
-		HostArch:                   stringPtr("x86_64"),
-		HostSecondaryArch:          stringPtr("x86"),
-		DeviceName:                 stringPtr("generic_arm64"),
-		DeviceProduct:              stringPtr("aosp_arm-eng"),
-		DeviceArch:                 stringPtr("arm64"),
-		DeviceArchVariant:          stringPtr("armv8-a"),
-		DeviceCpuVariant:           stringPtr("generic"),
-		DeviceAbi:                  []string{"arm64-v8a"},
-		DeviceSecondaryArch:        stringPtr("arm"),
-		DeviceSecondaryArchVariant: stringPtr("armv8-a"),
-		DeviceSecondaryCpuVariant:  stringPtr("generic"),
-		DeviceSecondaryAbi:         []string{"armeabi-v7a", "armeabi"},
-		DeviceMaxPageSizeSupported: stringPtr("4096"),
-		DevicePageSizeAgnostic:     boolPtr(false),
+		HostArch:                    stringPtr("x86_64"),
+		HostSecondaryArch:           stringPtr("x86"),
+		DeviceName:                  stringPtr("generic_arm64"),
+		DeviceProduct:               stringPtr("aosp_arm-eng"),
+		DeviceArch:                  stringPtr("arm64"),
+		DeviceArchVariant:           stringPtr("armv8-a"),
+		DeviceCpuVariant:            stringPtr("generic"),
+		DeviceAbi:                   []string{"arm64-v8a"},
+		DeviceSecondaryArch:         stringPtr("arm"),
+		DeviceSecondaryArchVariant:  stringPtr("armv8-a"),
+		DeviceSecondaryCpuVariant:   stringPtr("generic"),
+		DeviceSecondaryAbi:          []string{"armeabi-v7a", "armeabi"},
+		DeviceMaxPageSizeSupported:  stringPtr("4096"),
+		DeviceNoBionicPageSizeMacro: boolPtr(false),
 
 		AAPTConfig:          []string{"normal", "large", "xlarge", "hdpi", "xhdpi", "xxhdpi"},
 		AAPTPreferredConfig: stringPtr("xhdpi"),
diff --git a/android_sdk/sdk_repo_host.go b/android_sdk/sdk_repo_host.go
index 7212a07..373e883 100644
--- a/android_sdk/sdk_repo_host.go
+++ b/android_sdk/sdk_repo_host.go
@@ -165,10 +165,11 @@
 				Flag(dir.Join(ctx, strip).String())
 		}
 	} else {
+		llvmObjCopy := config.ClangPath(ctx, "bin/llvm-objcopy")
 		llvmStrip := config.ClangPath(ctx, "bin/llvm-strip")
 		llvmLib := config.ClangPath(ctx, "lib/x86_64-unknown-linux-gnu/libc++.so")
 		for _, strip := range s.properties.Strip_files {
-			cmd := builder.Command().Tool(llvmStrip).ImplicitTool(llvmLib)
+			cmd := builder.Command().Tool(llvmStrip).ImplicitTool(llvmLib).ImplicitTool(llvmObjCopy)
 			if !ctx.Windows() {
 				cmd.Flag("-x")
 			}
diff --git a/apex/apex.go b/apex/apex.go
index 56f3367..5d67c7a 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -2067,8 +2067,15 @@
 		switch depTag {
 		case sharedLibTag, jniLibTag:
 			isJniLib := depTag == jniLibTag
+			propertyName := "native_shared_libs"
+			if isJniLib {
+				propertyName = "jni_libs"
+			}
 			switch ch := child.(type) {
 			case *cc.Module:
+				if ch.IsStubs() {
+					ctx.PropertyErrorf(propertyName, "%q is a stub. Remove it from the list.", depName)
+				}
 				fi := apexFileForNativeLibrary(ctx, ch, vctx.handleSpecialLibs)
 				fi.isJniLib = isJniLib
 				vctx.filesInfo = append(vctx.filesInfo, fi)
@@ -2086,10 +2093,6 @@
 				vctx.filesInfo = append(vctx.filesInfo, fi)
 				return true // track transitive dependencies
 			default:
-				propertyName := "native_shared_libs"
-				if isJniLib {
-					propertyName = "jni_libs"
-				}
 				ctx.PropertyErrorf(propertyName, "%q is not a cc_library or cc_library_shared module", depName)
 			}
 		case executableTag:
diff --git a/apex/apex_test.go b/apex/apex_test.go
index f14ab8a..2f6acff 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -26,6 +26,7 @@
 	"testing"
 
 	"android/soong/aconfig/codegen"
+
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 
@@ -960,6 +961,32 @@
 	ensureListContains(t, names(apexManifestRule.Args["requireNativeLibs"]), "libfoo.shared_from_rust.so")
 }
 
+func TestApexShouldNotEmbedStubVariant(t *testing.T) {
+	testApexError(t, `module "myapex" .*: native_shared_libs: "libbar" is a stub`, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			vendor: true,
+			updatable: false,
+			native_shared_libs: ["libbar"], // should not add an LLNDK stub in a vendor apex
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		cc_library {
+			name: "libbar",
+			srcs: ["mylib.cpp"],
+			llndk: {
+				symbol_file: "libbar.map.txt",
+			}
+		}
+	`)
+}
+
 func TestApexCanUsePrivateApis(t *testing.T) {
 	ctx := testApex(t, `
 		apex {
diff --git a/bp2build/bp2build_product_config.go b/bp2build/bp2build_product_config.go
index 0d1e433..4c09d67 100644
--- a/bp2build/bp2build_product_config.go
+++ b/bp2build/bp2build_product_config.go
@@ -320,7 +320,7 @@
 		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:device_abi=%s\n", strings.Join(productVariables.DeviceAbi, ",")))
 		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:device_max_page_size_supported=%s\n", proptools.String(productVariables.DeviceMaxPageSizeSupported)))
 		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:device_name=%s\n", proptools.String(productVariables.DeviceName)))
-		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:device_page_size_agnostic=%t\n", proptools.Bool(productVariables.DevicePageSizeAgnostic)))
+		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:device_no_bionic_page_size_macro=%t\n", proptools.Bool(productVariables.DeviceNoBionicPageSizeMacro)))
 		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:device_product=%s\n", proptools.String(productVariables.DeviceProduct)))
 		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:device_platform=%s\n", label.String()))
 		result.WriteString(fmt.Sprintf("    --//build/bazel/product_config:enable_cfi=%t\n", proptools.BoolDefault(productVariables.EnableCFI, true)))
diff --git a/bpf/bpf.go b/bpf/bpf.go
index 58213aa..8c0d28b 100644
--- a/bpf/bpf.go
+++ b/bpf/bpf.go
@@ -206,6 +206,7 @@
 		}
 
 	}
+	ctx.SetProvider(blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: srcs.Strings()})
 }
 
 func (bpf *bpf) AndroidMk() android.AndroidMkData {
diff --git a/cc/cc.go b/cc/cc.go
index 6d8ca90..5d4ceb8 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -2322,6 +2322,7 @@
 	if c.testModule {
 		ctx.SetProvider(testing.TestModuleProviderKey, testing.TestModuleProviderData{})
 	}
+	ctx.SetProvider(blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: deps.GeneratedSources.Strings()})
 
 	aconfig.CollectTransitiveAconfigFiles(ctx, &c.transitiveAconfigFiles)
 
diff --git a/cc/config/arm64_device.go b/cc/config/arm64_device.go
index 12722a7..82beb29 100644
--- a/cc/config/arm64_device.go
+++ b/cc/config/arm64_device.go
@@ -103,7 +103,7 @@
 	exportedVars.ExportStringList("Arm64Cflags", arm64Cflags)
 	pctx.VariableFunc("Arm64Cflags", func(ctx android.PackageVarContext) string {
 		flags := arm64Cflags
-		if ctx.Config().PageSizeAgnostic() {
+		if ctx.Config().NoBionicPageSizeMacro() {
 			flags = append(flags, "-D__BIONIC_NO_PAGE_SIZE_MACRO")
 		}
 		return strings.Join(flags, " ")
diff --git a/cc/config/x86_64_device.go b/cc/config/x86_64_device.go
index 00a395f..e43848c 100644
--- a/cc/config/x86_64_device.go
+++ b/cc/config/x86_64_device.go
@@ -107,7 +107,7 @@
 	exportedVars.ExportStringList("X86_64Cflags", x86_64Cflags)
 	pctx.VariableFunc("X86_64Cflags", func(ctx android.PackageVarContext) string {
 		flags := x86_64Cflags
-		if ctx.Config().PageSizeAgnostic() {
+		if ctx.Config().NoBionicPageSizeMacro() {
 			flags = append(flags, "-D__BIONIC_NO_PAGE_SIZE_MACRO")
 		}
 		return strings.Join(flags, " ")
diff --git a/cc/ndk_library.go b/cc/ndk_library.go
index df775de..348c3bc 100644
--- a/cc/ndk_library.go
+++ b/cc/ndk_library.go
@@ -334,18 +334,12 @@
 		return false
 	}
 	// http://b/156513478
-	// http://b/277624006
-	// This step is expensive. We're not able to do anything with the outputs of
-	// this step yet (canDiffAbi is flagged off because libabigail isn't able to
-	// handle all our libraries), disable it. There's no sense in protecting
-	// against checking in code that breaks abidw since by the time any of this
-	// can be turned on we'll need to migrate to STG anyway.
-	return false
+	return config.ReleaseNdkAbiMonitored()
 }
 
 // Feature flag to disable diffing against prebuilts.
-func canDiffAbi() bool {
-	return false
+func canDiffAbi(config android.Config) bool {
+	return config.ReleaseNdkAbiMonitored()
 }
 
 func (this *stubDecorator) dumpAbi(ctx ModuleContext, symbolList android.Path) {
@@ -476,7 +470,7 @@
 	c.versionScriptPath = nativeAbiResult.versionScript
 	if canDumpAbi(ctx.Config()) {
 		c.dumpAbi(ctx, nativeAbiResult.symbolList)
-		if canDiffAbi() {
+		if canDiffAbi(ctx.Config()) {
 			c.diffAbi(ctx)
 		}
 	}
diff --git a/genrule/allowlists.go b/genrule/allowlists.go
index 65e4d88..926d6c0 100644
--- a/genrule/allowlists.go
+++ b/genrule/allowlists.go
@@ -26,6 +26,7 @@
 		"CtsApkVerityTestDebugFiles",
 		"aidl_camera_build_version",
 		"camera-its",
+		"chre_atoms_log.h",
 		// go/keep-sorted end
 	}
 
diff --git a/genrule/genrule.go b/genrule/genrule.go
index 8f2c047..6b11019 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -424,6 +424,7 @@
 		return srcFiles
 	}
 	srcFiles := addLabelsForInputs("srcs", g.properties.Srcs, g.properties.Exclude_srcs)
+	ctx.SetProvider(blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: srcFiles.Strings()})
 
 	var copyFrom android.Paths
 	var outputFiles android.WritablePaths
diff --git a/java/Android.bp b/java/Android.bp
index aa63aa3..d39a195 100644
--- a/java/Android.bp
+++ b/java/Android.bp
@@ -86,6 +86,7 @@
         "app_import_test.go",
         "app_set_test.go",
         "app_test.go",
+        "code_metadata_test.go",
         "bootclasspath_fragment_test.go",
         "device_host_converter_test.go",
         "dex_test.go",
diff --git a/java/base.go b/java/base.go
index 295340d..7e1381b 100644
--- a/java/base.go
+++ b/java/base.go
@@ -24,6 +24,7 @@
 
 	"github.com/google/blueprint/pathtools"
 	"github.com/google/blueprint/proptools"
+	"github.com/google/blueprint"
 
 	"android/soong/aconfig"
 	"android/soong/android"
@@ -1170,6 +1171,7 @@
 	uniqueSrcFiles = append(uniqueSrcFiles, uniqueJavaFiles...)
 	uniqueSrcFiles = append(uniqueSrcFiles, uniqueKtFiles...)
 	j.uniqueSrcFiles = uniqueSrcFiles
+	ctx.SetProvider(blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: uniqueSrcFiles.Strings()})
 
 	// We don't currently run annotation processors in turbine, which means we can't use turbine
 	// generated header jars when an annotation processor that generates API is enabled.  One
diff --git a/java/code_metadata_test.go b/java/code_metadata_test.go
new file mode 100644
index 0000000..4b05d9e
--- /dev/null
+++ b/java/code_metadata_test.go
@@ -0,0 +1,125 @@
+package java
+
+import (
+	"strings"
+	"testing"
+
+	"android/soong/android"
+	soongTesting "android/soong/testing"
+	"android/soong/testing/code_metadata_internal_proto"
+	"google.golang.org/protobuf/proto"
+)
+
+func TestCodeMetadata(t *testing.T) {
+	bp := `code_metadata {
+		name: "module-name",
+		teamId: "12345",
+		code: [
+			"foo",
+		]
+	}
+
+	java_sdk_library {
+		name: "foo",
+		srcs: ["a.java"],
+	}`
+	result := runCodeMetadataTest(t, android.FixtureExpectsNoErrors, bp)
+
+	module := result.ModuleForTests(
+		"module-name", "",
+	).Module().(*soongTesting.CodeMetadataModule)
+
+	// Check that the provider has the right contents
+	data := result.ModuleProvider(
+		module, soongTesting.CodeMetadataProviderKey,
+	).(soongTesting.CodeMetadataProviderData)
+	if !strings.HasSuffix(
+		data.IntermediatePath.String(), "/intermediateCodeMetadata.pb",
+	) {
+		t.Errorf(
+			"Missing intermediates path in provider: %s",
+			data.IntermediatePath.String(),
+		)
+	}
+
+	buildParamsSlice := module.BuildParamsForTests()
+	var metadata = ""
+	for _, params := range buildParamsSlice {
+		if params.Rule.String() == "android/soong/android.writeFile" {
+			metadata = params.Args["content"]
+		}
+	}
+
+	metadataList := make([]*code_metadata_internal_proto.CodeMetadataInternal_TargetOwnership, 0, 2)
+	teamId := "12345"
+	bpFilePath := "Android.bp"
+	targetName := "foo"
+	srcFile := []string{"a.java"}
+	expectedMetadataProto := code_metadata_internal_proto.CodeMetadataInternal_TargetOwnership{
+		TrendyTeamId: &teamId,
+		TargetName:   &targetName,
+		Path:         &bpFilePath,
+		SourceFiles:  srcFile,
+	}
+	metadataList = append(metadataList, &expectedMetadataProto)
+
+	CodeMetadataMetadata := code_metadata_internal_proto.CodeMetadataInternal{TargetOwnershipList: metadataList}
+	protoData, _ := proto.Marshal(&CodeMetadataMetadata)
+	rawData := string(protoData)
+	formattedData := strings.ReplaceAll(rawData, "\n", "\\n")
+	expectedMetadata := "'" + formattedData + "\\n'"
+
+	if metadata != expectedMetadata {
+		t.Errorf(
+			"Retrieved metadata: %s is not equal to expectedMetadata: %s", metadata,
+			expectedMetadata,
+		)
+	}
+
+	// Tests for all_test_spec singleton.
+	singleton := result.SingletonForTests("all_code_metadata")
+	rule := singleton.Rule("all_code_metadata_rule")
+	prebuiltOs := result.Config.PrebuiltOS()
+	expectedCmd := "out/soong/host/" + prebuiltOs + "/bin/metadata -rule code_metadata -inputFile out/soong/all_code_metadata_paths.rsp -outputFile out/soong/ownership/all_code_metadata.pb"
+	expectedOutputFile := "out/soong/ownership/all_code_metadata.pb"
+	expectedInputFile := "out/soong/.intermediates/module-name/intermediateCodeMetadata.pb"
+	if !strings.Contains(
+		strings.TrimSpace(rule.Output.String()),
+		expectedOutputFile,
+	) {
+		t.Errorf(
+			"Retrieved singletonOutputFile: %s is not equal to expectedSingletonOutputFile: %s",
+			rule.Output.String(), expectedOutputFile,
+		)
+	}
+
+	if !strings.Contains(
+		strings.TrimSpace(rule.Inputs[0].String()),
+		expectedInputFile,
+	) {
+		t.Errorf(
+			"Retrieved singletonInputFile: %s is not equal to expectedSingletonInputFile: %s",
+			rule.Inputs[0].String(), expectedInputFile,
+		)
+	}
+
+	if !strings.Contains(
+		strings.TrimSpace(rule.RuleParams.Command),
+		expectedCmd,
+	) {
+		t.Errorf(
+			"Retrieved cmd: %s doesn't contain expectedCmd: %s",
+			rule.RuleParams.Command, expectedCmd,
+		)
+	}
+}
+func runCodeMetadataTest(
+		t *testing.T, errorHandler android.FixtureErrorHandler, bp string,
+) *android.TestResult {
+	return android.GroupFixturePreparers(
+		soongTesting.PrepareForTestWithTestingBuildComponents, prepareForJavaTest,
+		PrepareForTestWithJavaSdkLibraryFiles, FixtureWithLastReleaseApis("foo"),
+	).
+		ExtendWithErrorHandler(errorHandler).
+		RunTestWithBp(t, bp)
+}
diff --git a/java/test_spec_test.go b/java/test_spec_test.go
index 39aff4c..7f06785 100644
--- a/java/test_spec_test.go
+++ b/java/test_spec_test.go
@@ -27,7 +27,7 @@
 	java_test {
 		name: "java-test-module-name-two",
 	}`
-	result := runTest(t, android.FixtureExpectsNoErrors, bp)
+	result := runTestSpecTest(t, android.FixtureExpectsNoErrors, bp)
 
 	module := result.ModuleForTests(
 		"module-name", "",
@@ -78,7 +78,7 @@
 
 	if metadata != expectedMetadata {
 		t.Errorf(
-			"Retrieved metadata: %s is not equal to expectedMetadata: %s", metadata,
+			"Retrieved metadata: %s doesn't contain expectedMetadata: %s", metadata,
 			expectedMetadata,
 		)
 	}
@@ -121,11 +121,11 @@
 	}
 }
 
-func runTest(
-	t *testing.T, errorHandler android.FixtureErrorHandler, bp string,
+func runTestSpecTest(
+		t *testing.T, errorHandler android.FixtureErrorHandler, bp string,
 ) *android.TestResult {
 	return android.GroupFixturePreparers(
-		soongTesting.PrepareForTestWithTestSpecBuildComponents,
+		soongTesting.PrepareForTestWithTestingBuildComponents,
 		PrepareForIntegrationTestWithJava,
 	).
 		ExtendWithErrorHandler(errorHandler).
diff --git a/python/python.go b/python/python.go
index 7d77ca7..b4617c8 100644
--- a/python/python.go
+++ b/python/python.go
@@ -430,6 +430,7 @@
 // GenerateAndroidBuildActions performs build actions common to all Python modules
 func (p *PythonLibraryModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	expandedSrcs := android.PathsForModuleSrcExcludes(ctx, p.properties.Srcs, p.properties.Exclude_srcs)
+	ctx.SetProvider(blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: expandedSrcs.Strings()})
 
 	// expand data files from "data" property.
 	expandedData := android.PathsForModuleSrc(ctx, p.properties.Data)
diff --git a/rust/rust.go b/rust/rust.go
index d4d33c7..3d51a13 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -951,6 +951,7 @@
 			sourceLib := sourceMod.(*Module).compiler.(*libraryDecorator)
 			mod.sourceProvider.setOutputFiles(sourceLib.sourceProvider.Srcs())
 		}
+		ctx.SetProvider(blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: mod.sourceProvider.Srcs().Strings()})
 	}
 
 	if mod.compiler != nil && !mod.compiler.Disabled() {
diff --git a/sh/sh_binary.go b/sh/sh_binary.go
index 6b40e3c..7962749 100644
--- a/sh/sh_binary.go
+++ b/sh/sh_binary.go
@@ -272,6 +272,7 @@
 		Output: s.outputFilePath,
 		Input:  s.sourceFilePath,
 	})
+	ctx.SetProvider(blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: []string{s.sourceFilePath.String()}})
 }
 
 func (s *ShBinary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
diff --git a/sysprop/sysprop_library.go b/sysprop/sysprop_library.go
index 8bf5f14..013624f 100644
--- a/sysprop/sysprop_library.go
+++ b/sysprop/sysprop_library.go
@@ -254,12 +254,13 @@
 // generated java_library will depend on these API files.
 func (m *syspropLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	baseModuleName := m.BaseModuleName()
-
-	for _, syspropFile := range android.PathsForModuleSrc(ctx, m.properties.Srcs) {
+	srcs := android.PathsForModuleSrc(ctx, m.properties.Srcs)
+	for _, syspropFile := range srcs {
 		if syspropFile.Ext() != ".sysprop" {
 			ctx.PropertyErrorf("srcs", "srcs contains non-sysprop file %q", syspropFile.String())
 		}
 	}
+	ctx.SetProvider(blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: srcs.Strings()})
 
 	if ctx.Failed() {
 		return
@@ -277,7 +278,7 @@
 	rule.Command().
 		BuiltTool("sysprop_api_dump").
 		Output(m.dumpedApiFile).
-		Inputs(android.PathsForModuleSrc(ctx, m.properties.Srcs))
+		Inputs(srcs)
 	rule.Build(baseModuleName+"_api_dump", baseModuleName+" api dump")
 
 	// check API rule
diff --git a/testing/Android.bp b/testing/Android.bp
index 18dccb3..43040b0 100644
--- a/testing/Android.bp
+++ b/testing/Android.bp
@@ -8,11 +8,14 @@
     deps: [
         "blueprint",
         "soong-android",
+        "soong-testing-code_metadata_internal_proto",
         "soong-testing-test_spec_proto",
 
     ],
     srcs: [
+        "all_code_metadata.go",
         "all_test_specs.go",
+        "code_metadata.go",
         "test_spec.go",
         "init.go",
         "test.go",
diff --git a/testing/OWNERS b/testing/OWNERS
new file mode 100644
index 0000000..03bcdf1
--- /dev/null
+++ b/testing/OWNERS
@@ -0,0 +1,4 @@
+dariofreni@google.com
+joeo@google.com
+ronish@google.com
+caditya@google.com
diff --git a/testing/all_code_metadata.go b/testing/all_code_metadata.go
new file mode 100644
index 0000000..16d7aae
--- /dev/null
+++ b/testing/all_code_metadata.go
@@ -0,0 +1,51 @@
+package testing
+
+import (
+	"android/soong/android"
+)
+
+const fileContainingCodeMetadataFilePaths = "all_code_metadata_paths.rsp"
+const allCodeMetadataFile = "all_code_metadata.pb"
+
+func AllCodeMetadataFactory() android.Singleton {
+	return &allCodeMetadataSingleton{}
+}
+
+type allCodeMetadataSingleton struct {
+	// Path where the collected metadata is stored after successful validation.
+	outputPath android.OutputPath
+}
+
+func (this *allCodeMetadataSingleton) GenerateBuildActions(ctx android.SingletonContext) {
+	var intermediateMetadataPaths android.Paths
+
+	ctx.VisitAllModules(
+		func(module android.Module) {
+			if !ctx.ModuleHasProvider(module, CodeMetadataProviderKey) {
+				return
+			}
+			intermediateMetadataPaths = append(
+				intermediateMetadataPaths, ctx.ModuleProvider(
+					module, CodeMetadataProviderKey,
+				).(CodeMetadataProviderData).IntermediatePath,
+			)
+		},
+	)
+
+	rspFile := android.PathForOutput(ctx, fileContainingCodeMetadataFilePaths)
+	this.outputPath = android.PathForOutput(ctx, ownershipDirectory, allCodeMetadataFile)
+
+	rule := android.NewRuleBuilder(pctx, ctx)
+	cmd := rule.Command().
+		BuiltTool("metadata").
+		FlagWithArg("-rule ", "code_metadata").
+		FlagWithRspFileInputList("-inputFile ", rspFile, intermediateMetadataPaths)
+	cmd.FlagWithOutput("-outputFile ", this.outputPath)
+	rule.Build("all_code_metadata_rule", "Generate all code metadata")
+
+	ctx.Phony("all_code_metadata", this.outputPath)
+}
+
+func (this *allCodeMetadataSingleton) MakeVars(ctx android.MakeVarsContext) {
+	ctx.DistForGoal("code_metadata", this.outputPath)
+}
diff --git a/testing/code_metadata.go b/testing/code_metadata.go
new file mode 100644
index 0000000..4550283
--- /dev/null
+++ b/testing/code_metadata.go
@@ -0,0 +1,139 @@
+// Copyright 2020 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 testing
+
+import (
+	"path/filepath"
+
+	"android/soong/android"
+	"android/soong/testing/code_metadata_internal_proto"
+	"github.com/google/blueprint"
+	"google.golang.org/protobuf/proto"
+)
+
+func CodeMetadataFactory() android.Module {
+	module := &CodeMetadataModule{}
+
+	android.InitAndroidModule(module)
+	android.InitDefaultableModule(module)
+	module.AddProperties(&module.properties)
+
+	return module
+}
+
+type CodeMetadataModule struct {
+	android.ModuleBase
+	android.DefaultableModuleBase
+	android.BazelModuleBase
+
+	// Properties for "code_metadata"
+	properties struct {
+		// Specifies the name of the code_config.
+		Name string
+		// Specifies the team ID.
+		TeamId string
+		// Specifies the list of modules that this code_metadata covers.
+		Code []string
+		// An optional field to specify if multiple ownerships for source files is allowed.
+		MultiOwnership bool
+	}
+}
+
+type codeDepTagType struct {
+	blueprint.BaseDependencyTag
+}
+
+var codeDepTag = codeDepTagType{}
+
+func (module *CodeMetadataModule) DepsMutator(ctx android.BottomUpMutatorContext) {
+	// Validate Properties
+	if len(module.properties.TeamId) == 0 {
+		ctx.PropertyErrorf(
+			"TeamId",
+			"Team Id not found in the code_metadata module. Hint: Maybe the teamId property hasn't been properly specified.",
+		)
+	}
+	if !isInt(module.properties.TeamId) {
+		ctx.PropertyErrorf(
+			"TeamId", "Invalid value for Team ID. The Team ID must be an integer.",
+		)
+	}
+	if len(module.properties.Code) == 0 {
+		ctx.PropertyErrorf(
+			"Code",
+			"Targets to be attributed cannot be empty. Hint: Maybe the code property hasn't been properly specified.",
+		)
+	}
+	ctx.AddDependency(ctx.Module(), codeDepTag, module.properties.Code...)
+}
+
+// Provider published by CodeMetadata
+type CodeMetadataProviderData struct {
+	IntermediatePath android.WritablePath
+}
+
+var CodeMetadataProviderKey = blueprint.NewProvider(CodeMetadataProviderData{})
+
+func (module *CodeMetadataModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	metadataList := make(
+		[]*code_metadata_internal_proto.CodeMetadataInternal_TargetOwnership, 0,
+		len(module.properties.Code),
+	)
+	bpFilePath := filepath.Join(ctx.ModuleDir(), ctx.BlueprintsFile())
+
+	for _, m := range ctx.GetDirectDepsWithTag(codeDepTag) {
+		targetName := m.Name()
+		var moduleSrcs []string
+		if ctx.OtherModuleHasProvider(m, blueprint.SrcsFileProviderKey) {
+			moduleSrcs = ctx.OtherModuleProvider(
+				m, blueprint.SrcsFileProviderKey,
+			).(blueprint.SrcsFileProviderData).SrcPaths
+		}
+		if module.properties.MultiOwnership {
+			metadata := &code_metadata_internal_proto.CodeMetadataInternal_TargetOwnership{
+				TargetName:     &targetName,
+				TrendyTeamId:   &module.properties.TeamId,
+				Path:           &bpFilePath,
+				MultiOwnership: &module.properties.MultiOwnership,
+				SourceFiles:    moduleSrcs,
+			}
+			metadataList = append(metadataList, metadata)
+		} else {
+			metadata := &code_metadata_internal_proto.CodeMetadataInternal_TargetOwnership{
+				TargetName:   &targetName,
+				TrendyTeamId: &module.properties.TeamId,
+				Path:         &bpFilePath,
+				SourceFiles:  moduleSrcs,
+			}
+			metadataList = append(metadataList, metadata)
+		}
+
+	}
+	codeMetadata := &code_metadata_internal_proto.CodeMetadataInternal{TargetOwnershipList: metadataList}
+	protoData, err := proto.Marshal(codeMetadata)
+	if err != nil {
+		ctx.ModuleErrorf("Error marshaling code metadata: %s", err.Error())
+		return
+	}
+	intermediatePath := android.PathForModuleOut(
+		ctx, "intermediateCodeMetadata.pb",
+	)
+	android.WriteFileRule(ctx, intermediatePath, string(protoData))
+
+	ctx.SetProvider(
+		CodeMetadataProviderKey,
+		CodeMetadataProviderData{IntermediatePath: intermediatePath},
+	)
+}
diff --git a/testing/code_metadata_internal_proto/Android.bp b/testing/code_metadata_internal_proto/Android.bp
new file mode 100644
index 0000000..a534cc2
--- /dev/null
+++ b/testing/code_metadata_internal_proto/Android.bp
@@ -0,0 +1,29 @@
+// 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 {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+bootstrap_go_package {
+    name: "soong-testing-code_metadata_internal_proto",
+    pkgPath: "android/soong/testing/code_metadata_internal_proto",
+    deps: [
+            "golang-protobuf-reflect-protoreflect",
+            "golang-protobuf-runtime-protoimpl",
+        ],
+    srcs: [
+        "code_metadata_internal.pb.go",
+    ],
+}
diff --git a/testing/code_metadata_internal_proto/OWNERS b/testing/code_metadata_internal_proto/OWNERS
new file mode 100644
index 0000000..03bcdf1
--- /dev/null
+++ b/testing/code_metadata_internal_proto/OWNERS
@@ -0,0 +1,4 @@
+dariofreni@google.com
+joeo@google.com
+ronish@google.com
+caditya@google.com
diff --git a/testing/code_metadata_internal_proto/code_metadata_internal.pb.go b/testing/code_metadata_internal_proto/code_metadata_internal.pb.go
new file mode 100644
index 0000000..ecb8b86
--- /dev/null
+++ b/testing/code_metadata_internal_proto/code_metadata_internal.pb.go
@@ -0,0 +1,277 @@
+// 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.
+
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.30.0
+// 	protoc        v3.21.12
+// source: code_metadata_internal.proto
+
+package code_metadata_internal_proto
+
+import (
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	reflect "reflect"
+	sync "sync"
+)
+
+const (
+	// Verify that this generated code is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+	// Verify that runtime/protoimpl is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+type CodeMetadataInternal struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// List of all code targets and their metadata.
+	TargetOwnershipList []*CodeMetadataInternal_TargetOwnership `protobuf:"bytes,1,rep,name=target_ownership_list,json=targetOwnershipList" json:"target_ownership_list,omitempty"`
+}
+
+func (x *CodeMetadataInternal) Reset() {
+	*x = CodeMetadataInternal{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_code_metadata_internal_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *CodeMetadataInternal) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*CodeMetadataInternal) ProtoMessage() {}
+
+func (x *CodeMetadataInternal) ProtoReflect() protoreflect.Message {
+	mi := &file_code_metadata_internal_proto_msgTypes[0]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use CodeMetadataInternal.ProtoReflect.Descriptor instead.
+func (*CodeMetadataInternal) Descriptor() ([]byte, []int) {
+	return file_code_metadata_internal_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *CodeMetadataInternal) GetTargetOwnershipList() []*CodeMetadataInternal_TargetOwnership {
+	if x != nil {
+		return x.TargetOwnershipList
+	}
+	return nil
+}
+
+type CodeMetadataInternal_TargetOwnership struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// REQUIRED: Name of the build target
+	TargetName *string `protobuf:"bytes,1,opt,name=target_name,json=targetName" json:"target_name,omitempty"`
+	// REQUIRED: Code location of the target.
+	// To be used to support legacy/backup systems that use OWNERS file and is
+	// also required for our dashboard to support per code location basis UI
+	Path *string `protobuf:"bytes,2,opt,name=path" json:"path,omitempty"`
+	// REQUIRED: Team ID of the team that owns this target.
+	TrendyTeamId *string `protobuf:"bytes,3,opt,name=trendy_team_id,json=trendyTeamId" json:"trendy_team_id,omitempty"`
+	// OPTIONAL: The src files of the target.
+	// To be used to determine ownership of a file for ownership
+	SourceFiles []string `protobuf:"bytes,4,rep,name=source_files,json=sourceFiles" json:"source_files,omitempty"`
+	// OPTIONAL: Specify if multiple ownerships of the source files are allowed.
+	MultiOwnership *bool `protobuf:"varint,5,opt,name=multi_ownership,json=multiOwnership" json:"multi_ownership,omitempty"`
+}
+
+func (x *CodeMetadataInternal_TargetOwnership) Reset() {
+	*x = CodeMetadataInternal_TargetOwnership{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_code_metadata_internal_proto_msgTypes[1]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *CodeMetadataInternal_TargetOwnership) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*CodeMetadataInternal_TargetOwnership) ProtoMessage() {}
+
+func (x *CodeMetadataInternal_TargetOwnership) ProtoReflect() protoreflect.Message {
+	mi := &file_code_metadata_internal_proto_msgTypes[1]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use CodeMetadataInternal_TargetOwnership.ProtoReflect.Descriptor instead.
+func (*CodeMetadataInternal_TargetOwnership) Descriptor() ([]byte, []int) {
+	return file_code_metadata_internal_proto_rawDescGZIP(), []int{0, 0}
+}
+
+func (x *CodeMetadataInternal_TargetOwnership) GetTargetName() string {
+	if x != nil && x.TargetName != nil {
+		return *x.TargetName
+	}
+	return ""
+}
+
+func (x *CodeMetadataInternal_TargetOwnership) GetPath() string {
+	if x != nil && x.Path != nil {
+		return *x.Path
+	}
+	return ""
+}
+
+func (x *CodeMetadataInternal_TargetOwnership) GetTrendyTeamId() string {
+	if x != nil && x.TrendyTeamId != nil {
+		return *x.TrendyTeamId
+	}
+	return ""
+}
+
+func (x *CodeMetadataInternal_TargetOwnership) GetSourceFiles() []string {
+	if x != nil {
+		return x.SourceFiles
+	}
+	return nil
+}
+
+func (x *CodeMetadataInternal_TargetOwnership) GetMultiOwnership() bool {
+	if x != nil && x.MultiOwnership != nil {
+		return *x.MultiOwnership
+	}
+	return false
+}
+
+var File_code_metadata_internal_proto protoreflect.FileDescriptor
+
+var file_code_metadata_internal_proto_rawDesc = []byte{
+	0x0a, 0x1c, 0x63, 0x6f, 0x64, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f,
+	0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1c,
+	0x63, 0x6f, 0x64, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x69, 0x6e,
+	0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xc9, 0x02, 0x0a,
+	0x14, 0x43, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x49, 0x6e, 0x74,
+	0x65, 0x72, 0x6e, 0x61, 0x6c, 0x12, 0x76, 0x0a, 0x15, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f,
+	0x6f, 0x77, 0x6e, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x01,
+	0x20, 0x03, 0x28, 0x0b, 0x32, 0x42, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61,
+	0x64, 0x61, 0x74, 0x61, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x70, 0x72,
+	0x6f, 0x74, 0x6f, 0x2e, 0x43, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61,
+	0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4f,
+	0x77, 0x6e, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x52, 0x13, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74,
+	0x4f, 0x77, 0x6e, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x4c, 0x69, 0x73, 0x74, 0x1a, 0xb8, 0x01,
+	0x0a, 0x0f, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x73, 0x68, 0x69,
+	0x70, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65,
+	0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4e, 0x61,
+	0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
+	0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x24, 0x0a, 0x0e, 0x74, 0x72, 0x65, 0x6e, 0x64, 0x79,
+	0x5f, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c,
+	0x74, 0x72, 0x65, 0x6e, 0x64, 0x79, 0x54, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c,
+	0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03,
+	0x28, 0x09, 0x52, 0x0b, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x12,
+	0x27, 0x0a, 0x0f, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x73, 0x68,
+	0x69, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x4f,
+	0x77, 0x6e, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x42, 0x34, 0x5a, 0x32, 0x61, 0x6e, 0x64, 0x72,
+	0x6f, 0x69, 0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e,
+	0x67, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f,
+	0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+}
+
+var (
+	file_code_metadata_internal_proto_rawDescOnce sync.Once
+	file_code_metadata_internal_proto_rawDescData = file_code_metadata_internal_proto_rawDesc
+)
+
+func file_code_metadata_internal_proto_rawDescGZIP() []byte {
+	file_code_metadata_internal_proto_rawDescOnce.Do(func() {
+		file_code_metadata_internal_proto_rawDescData = protoimpl.X.CompressGZIP(file_code_metadata_internal_proto_rawDescData)
+	})
+	return file_code_metadata_internal_proto_rawDescData
+}
+
+var file_code_metadata_internal_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
+var file_code_metadata_internal_proto_goTypes = []interface{}{
+	(*CodeMetadataInternal)(nil),                 // 0: code_metadata_internal_proto.CodeMetadataInternal
+	(*CodeMetadataInternal_TargetOwnership)(nil), // 1: code_metadata_internal_proto.CodeMetadataInternal.TargetOwnership
+}
+var file_code_metadata_internal_proto_depIdxs = []int32{
+	1, // 0: code_metadata_internal_proto.CodeMetadataInternal.target_ownership_list:type_name -> code_metadata_internal_proto.CodeMetadataInternal.TargetOwnership
+	1, // [1:1] is the sub-list for method output_type
+	1, // [1:1] is the sub-list for method input_type
+	1, // [1:1] is the sub-list for extension type_name
+	1, // [1:1] is the sub-list for extension extendee
+	0, // [0:1] is the sub-list for field type_name
+}
+
+func init() { file_code_metadata_internal_proto_init() }
+func file_code_metadata_internal_proto_init() {
+	if File_code_metadata_internal_proto != nil {
+		return
+	}
+	if !protoimpl.UnsafeEnabled {
+		file_code_metadata_internal_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*CodeMetadataInternal); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_code_metadata_internal_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*CodeMetadataInternal_TargetOwnership); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: file_code_metadata_internal_proto_rawDesc,
+			NumEnums:      0,
+			NumMessages:   2,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_code_metadata_internal_proto_goTypes,
+		DependencyIndexes: file_code_metadata_internal_proto_depIdxs,
+		MessageInfos:      file_code_metadata_internal_proto_msgTypes,
+	}.Build()
+	File_code_metadata_internal_proto = out.File
+	file_code_metadata_internal_proto_rawDesc = nil
+	file_code_metadata_internal_proto_goTypes = nil
+	file_code_metadata_internal_proto_depIdxs = nil
+}
diff --git a/testing/code_metadata_internal_proto/code_metadata_internal.proto b/testing/code_metadata_internal_proto/code_metadata_internal.proto
new file mode 100644
index 0000000..14edc0f
--- /dev/null
+++ b/testing/code_metadata_internal_proto/code_metadata_internal.proto
@@ -0,0 +1,40 @@
+// 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.
+
+syntax = "proto2";
+package code_metadata_internal_proto;
+option go_package = "android/soong/testing/code_metadata_internal_proto";
+
+message CodeMetadataInternal {
+
+  message TargetOwnership {
+    // REQUIRED: Name of the build target
+    optional string target_name = 1;
+
+    // REQUIRED: Code location of the target.
+    // To be used to support legacy/backup systems that use OWNERS file and is
+    // also required for our dashboard to support per code location basis UI
+    optional string path = 2;
+
+    // REQUIRED: Team ID of the team that owns this target.
+    optional string trendy_team_id = 3;
+
+    // OPTIONAL: The src files of the target.
+    // To be used to determine ownership of a file for ownership
+    repeated string source_files = 4;
+
+    // OPTIONAL: Specify if multiple ownerships of the source files are allowed.
+    optional bool multi_ownership = 5;
+  }
+
+  // List of all code targets and their metadata.
+  repeated TargetOwnership target_ownership_list = 1;
+}
diff --git a/testing/code_metadata_internal_proto/go.mod b/testing/code_metadata_internal_proto/go.mod
new file mode 100644
index 0000000..7e9129d
--- /dev/null
+++ b/testing/code_metadata_internal_proto/go.mod
@@ -0,0 +1,3 @@
+module android/soong/testing/code_metadata_internal_proto
+
+go 1.18
diff --git a/testing/code_metadata_internal_proto/regen.sh b/testing/code_metadata_internal_proto/regen.sh
new file mode 100644
index 0000000..f101a02
--- /dev/null
+++ b/testing/code_metadata_internal_proto/regen.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+aprotoc --go_out=paths=source_relative:. code_metadata_internal.proto
diff --git a/testing/code_metadata_proto/Android.bp b/testing/code_metadata_proto/Android.bp
new file mode 100644
index 0000000..8fcca19
--- /dev/null
+++ b/testing/code_metadata_proto/Android.bp
@@ -0,0 +1,29 @@
+// 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 {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+bootstrap_go_package {
+    name: "soong-testing-code_metadata_proto",
+    pkgPath: "android/soong/testing/code_metadata_proto",
+    deps: [
+            "golang-protobuf-reflect-protoreflect",
+            "golang-protobuf-runtime-protoimpl",
+        ],
+    srcs: [
+        "code_metadata.pb.go",
+    ],
+}
diff --git a/testing/code_metadata_proto/OWNERS b/testing/code_metadata_proto/OWNERS
new file mode 100644
index 0000000..03bcdf1
--- /dev/null
+++ b/testing/code_metadata_proto/OWNERS
@@ -0,0 +1,4 @@
+dariofreni@google.com
+joeo@google.com
+ronish@google.com
+caditya@google.com
diff --git a/testing/code_metadata_proto/code_metadata.pb.go b/testing/code_metadata_proto/code_metadata.pb.go
new file mode 100644
index 0000000..711bf7a
--- /dev/null
+++ b/testing/code_metadata_proto/code_metadata.pb.go
@@ -0,0 +1,263 @@
+// 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.
+
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.30.0
+// 	protoc        v3.21.12
+// source: code_metadata.proto
+
+package code_metadata_proto
+
+import (
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	reflect "reflect"
+	sync "sync"
+)
+
+const (
+	// Verify that this generated code is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+	// Verify that runtime/protoimpl is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+type CodeMetadata struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// List of all code targets and their metadata.
+	TargetOwnershipList []*CodeMetadata_TargetOwnership `protobuf:"bytes,1,rep,name=target_ownership_list,json=targetOwnershipList" json:"target_ownership_list,omitempty"`
+}
+
+func (x *CodeMetadata) Reset() {
+	*x = CodeMetadata{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_code_metadata_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *CodeMetadata) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*CodeMetadata) ProtoMessage() {}
+
+func (x *CodeMetadata) ProtoReflect() protoreflect.Message {
+	mi := &file_code_metadata_proto_msgTypes[0]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use CodeMetadata.ProtoReflect.Descriptor instead.
+func (*CodeMetadata) Descriptor() ([]byte, []int) {
+	return file_code_metadata_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *CodeMetadata) GetTargetOwnershipList() []*CodeMetadata_TargetOwnership {
+	if x != nil {
+		return x.TargetOwnershipList
+	}
+	return nil
+}
+
+type CodeMetadata_TargetOwnership struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// REQUIRED: Name of the build target
+	TargetName *string `protobuf:"bytes,1,opt,name=target_name,json=targetName" json:"target_name,omitempty"`
+	// REQUIRED: Code location of the target.
+	// To be used to support legacy/backup systems that use OWNERS file and is
+	// also required for our dashboard to support per code location basis UI
+	Path *string `protobuf:"bytes,2,opt,name=path" json:"path,omitempty"`
+	// REQUIRED: Team ID of the team that owns this target.
+	TrendyTeamId *string `protobuf:"bytes,3,opt,name=trendy_team_id,json=trendyTeamId" json:"trendy_team_id,omitempty"`
+	// OPTIONAL: The src files of the target.
+	// To be used to determine ownership of a file for ownership
+	SourceFiles []string `protobuf:"bytes,4,rep,name=source_files,json=sourceFiles" json:"source_files,omitempty"`
+}
+
+func (x *CodeMetadata_TargetOwnership) Reset() {
+	*x = CodeMetadata_TargetOwnership{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_code_metadata_proto_msgTypes[1]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *CodeMetadata_TargetOwnership) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*CodeMetadata_TargetOwnership) ProtoMessage() {}
+
+func (x *CodeMetadata_TargetOwnership) ProtoReflect() protoreflect.Message {
+	mi := &file_code_metadata_proto_msgTypes[1]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use CodeMetadata_TargetOwnership.ProtoReflect.Descriptor instead.
+func (*CodeMetadata_TargetOwnership) Descriptor() ([]byte, []int) {
+	return file_code_metadata_proto_rawDescGZIP(), []int{0, 0}
+}
+
+func (x *CodeMetadata_TargetOwnership) GetTargetName() string {
+	if x != nil && x.TargetName != nil {
+		return *x.TargetName
+	}
+	return ""
+}
+
+func (x *CodeMetadata_TargetOwnership) GetPath() string {
+	if x != nil && x.Path != nil {
+		return *x.Path
+	}
+	return ""
+}
+
+func (x *CodeMetadata_TargetOwnership) GetTrendyTeamId() string {
+	if x != nil && x.TrendyTeamId != nil {
+		return *x.TrendyTeamId
+	}
+	return ""
+}
+
+func (x *CodeMetadata_TargetOwnership) GetSourceFiles() []string {
+	if x != nil {
+		return x.SourceFiles
+	}
+	return nil
+}
+
+var File_code_metadata_proto protoreflect.FileDescriptor
+
+var file_code_metadata_proto_rawDesc = []byte{
+	0x0a, 0x13, 0x63, 0x6f, 0x64, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e,
+	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x13, 0x63, 0x6f, 0x64, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61,
+	0x64, 0x61, 0x74, 0x61, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x87, 0x02, 0x0a, 0x0c, 0x43,
+	0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x65, 0x0a, 0x15, 0x74,
+	0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x5f,
+	0x6c, 0x69, 0x73, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x63, 0x6f, 0x64,
+	0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+	0x2e, 0x43, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x54, 0x61,
+	0x72, 0x67, 0x65, 0x74, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x52, 0x13, 0x74,
+	0x61, 0x72, 0x67, 0x65, 0x74, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x4c, 0x69,
+	0x73, 0x74, 0x1a, 0x8f, 0x01, 0x0a, 0x0f, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4f, 0x77, 0x6e,
+	0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74,
+	0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x61, 0x72,
+	0x67, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18,
+	0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x24, 0x0a, 0x0e, 0x74,
+	0x72, 0x65, 0x6e, 0x64, 0x79, 0x5f, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20,
+	0x01, 0x28, 0x09, 0x52, 0x0c, 0x74, 0x72, 0x65, 0x6e, 0x64, 0x79, 0x54, 0x65, 0x61, 0x6d, 0x49,
+	0x64, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x66, 0x69, 0x6c, 0x65,
+	0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x46,
+	0x69, 0x6c, 0x65, 0x73, 0x42, 0x2b, 0x5a, 0x29, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f,
+	0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x2f, 0x63, 0x6f,
+	0x64, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x70, 0x72, 0x6f, 0x74,
+	0x6f,
+}
+
+var (
+	file_code_metadata_proto_rawDescOnce sync.Once
+	file_code_metadata_proto_rawDescData = file_code_metadata_proto_rawDesc
+)
+
+func file_code_metadata_proto_rawDescGZIP() []byte {
+	file_code_metadata_proto_rawDescOnce.Do(func() {
+		file_code_metadata_proto_rawDescData = protoimpl.X.CompressGZIP(file_code_metadata_proto_rawDescData)
+	})
+	return file_code_metadata_proto_rawDescData
+}
+
+var file_code_metadata_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
+var file_code_metadata_proto_goTypes = []interface{}{
+	(*CodeMetadata)(nil),                 // 0: code_metadata_proto.CodeMetadata
+	(*CodeMetadata_TargetOwnership)(nil), // 1: code_metadata_proto.CodeMetadata.TargetOwnership
+}
+var file_code_metadata_proto_depIdxs = []int32{
+	1, // 0: code_metadata_proto.CodeMetadata.target_ownership_list:type_name -> code_metadata_proto.CodeMetadata.TargetOwnership
+	1, // [1:1] is the sub-list for method output_type
+	1, // [1:1] is the sub-list for method input_type
+	1, // [1:1] is the sub-list for extension type_name
+	1, // [1:1] is the sub-list for extension extendee
+	0, // [0:1] is the sub-list for field type_name
+}
+
+func init() { file_code_metadata_proto_init() }
+func file_code_metadata_proto_init() {
+	if File_code_metadata_proto != nil {
+		return
+	}
+	if !protoimpl.UnsafeEnabled {
+		file_code_metadata_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*CodeMetadata); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_code_metadata_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*CodeMetadata_TargetOwnership); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: file_code_metadata_proto_rawDesc,
+			NumEnums:      0,
+			NumMessages:   2,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_code_metadata_proto_goTypes,
+		DependencyIndexes: file_code_metadata_proto_depIdxs,
+		MessageInfos:      file_code_metadata_proto_msgTypes,
+	}.Build()
+	File_code_metadata_proto = out.File
+	file_code_metadata_proto_rawDesc = nil
+	file_code_metadata_proto_goTypes = nil
+	file_code_metadata_proto_depIdxs = nil
+}
diff --git a/testing/code_metadata_proto/code_metadata.proto b/testing/code_metadata_proto/code_metadata.proto
new file mode 100644
index 0000000..2548363
--- /dev/null
+++ b/testing/code_metadata_proto/code_metadata.proto
@@ -0,0 +1,37 @@
+// 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.
+
+syntax = "proto2";
+package code_metadata_proto;
+option go_package = "android/soong/testing/code_metadata_proto";
+
+message CodeMetadata {
+
+  message TargetOwnership {
+    // REQUIRED: Name of the build target
+    optional string target_name = 1;
+
+    // REQUIRED: Code location of the target.
+    // To be used to support legacy/backup systems that use OWNERS file and is
+    // also required for our dashboard to support per code location basis UI
+    optional string path = 2;
+
+    // REQUIRED: Team ID of the team that owns this target.
+    optional string trendy_team_id = 3;
+
+    // OPTIONAL: The src files of the target.
+    // To be used to determine ownership of a file for ownership
+    repeated string source_files = 4;
+  }
+
+  // List of all code targets and their metadata.
+  repeated TargetOwnership target_ownership_list = 1;
+}
diff --git a/testing/code_metadata_proto/go.mod b/testing/code_metadata_proto/go.mod
new file mode 100644
index 0000000..ada2411
--- /dev/null
+++ b/testing/code_metadata_proto/go.mod
@@ -0,0 +1,3 @@
+module android/soong/testing/code_metadata_proto
+
+go 1.18
diff --git a/testing/code_metadata_proto/regen.sh b/testing/code_metadata_proto/regen.sh
new file mode 100644
index 0000000..ffe06f7
--- /dev/null
+++ b/testing/code_metadata_proto/regen.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+aprotoc --go_out=paths=source_relative:. code_metadata.proto
diff --git a/testing/init.go b/testing/init.go
index 206b430..edcbf59 100644
--- a/testing/init.go
+++ b/testing/init.go
@@ -28,6 +28,8 @@
 }
 
 func RegisterBuildComponents(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("code_metadata", CodeMetadataFactory)
 	ctx.RegisterModuleType("test_spec", TestSpecFactory)
+	ctx.RegisterParallelSingletonType("all_code_metadata", AllCodeMetadataFactory)
 	ctx.RegisterParallelSingletonType("all_test_specs", AllTestSpecsFactory)
 }
diff --git a/testing/test.go b/testing/test.go
index 44824e4..cd97a8f 100644
--- a/testing/test.go
+++ b/testing/test.go
@@ -18,4 +18,4 @@
 	"android/soong/android"
 )
 
-var PrepareForTestWithTestSpecBuildComponents = android.FixtureRegisterWithContext(RegisterBuildComponents)
+var PrepareForTestWithTestingBuildComponents = android.FixtureRegisterWithContext(RegisterBuildComponents)
diff --git a/testing/test_spec_proto/go.mod b/testing/test_spec_proto/go.mod
new file mode 100644
index 0000000..b581aac
--- /dev/null
+++ b/testing/test_spec_proto/go.mod
@@ -0,0 +1,3 @@
+module android/soong/testing/test_spec_proto
+
+go 1.18