Add team property to all modules.

This allows vendors (like google) to specify which team owns the test
module and code.

Team is a commonProperty on modules and points to the designate "team"
module.  The DepsMutator adds the dependency on the "team" module and
"GenerateBuildActions" write the team data to intermediate files.

A new singleton rule, all_teams visits all modules and writes out
the proto containing the team for each module.
If a module doesn't have a team, then it finds the package in the
blueprint file and parent directory blueprint files that have a
default_team and uses that team.

*** BRANCH DIFF ***
*** BRANCH DIFF ***
   Some small diffs in writing proto file on udc-branch.
   i.e. write as textproto instead of binary and only write modules
   with teams or default teams from package, not all modules.
   This is because udc doesn't have the newer WriteFile rule from
   raw_files: https://source.corp.google.com/h/android/platform/build/soong/+/31a674571e53f12dffb97698e65b9eee6ae0ebbc
*** BRANCH DIFF ***
*** BRANCH DIFF ***

Test: m all_teams
Test: go test ./python ./java ./cc ./rust ./android
Test: added team to HelloWorldHostTest and built the new asciiproto target
Test: added package default_team and checkout output proto.
Change-Id: I5c07bf489de460a04fc540f5fff0394f39f574a7
Merged-In: I5c07bf489de460a04fc540f5fff0394f39f574a7
diff --git a/android/Android.bp b/android/Android.bp
index 641c438..b13d7cd 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -11,6 +11,7 @@
         "blueprint-metrics",
         "sbox_proto",
         "soong",
+        "soong-android_team_proto",
         "soong-android-soongconfig",
         "soong-bazel",
         "soong-cquery",
@@ -28,6 +29,7 @@
         "androidmk-parser",
     ],
     srcs: [
+        "all_teams.go",
         "androidmk.go",
         "apex.go",
         "api_domain.go",
@@ -88,6 +90,7 @@
         "singleton.go",
         "singleton_module.go",
         "soong_config_modules.go",
+        "team.go",
         "test_asserts.go",
         "test_suites.go",
         "testing.go",
diff --git a/android/all_teams.go b/android/all_teams.go
new file mode 100644
index 0000000..d061649
--- /dev/null
+++ b/android/all_teams.go
@@ -0,0 +1,151 @@
+package android
+
+import (
+	"android/soong/android/team_proto"
+	"path/filepath"
+
+	"github.com/google/blueprint/proptools"
+	"google.golang.org/protobuf/encoding/prototext"
+	"google.golang.org/protobuf/proto"
+)
+
+const ownershipDirectory = "ownership"
+const allTeamsFile = "all_teams.pb"
+
+func AllTeamsFactory() Singleton {
+	return &allTeamsSingleton{}
+}
+
+func init() {
+	registerAllTeamBuildComponents(InitRegistrationContext)
+}
+
+func registerAllTeamBuildComponents(ctx RegistrationContext) {
+	ctx.RegisterSingletonType("all_teams", AllTeamsFactory)
+}
+
+// For each module, list the team or the bpFile the module is defined in.
+type moduleTeamInfo struct {
+	teamName string
+	bpFile   string
+}
+
+type allTeamsSingleton struct {
+	// Path where the collected metadata is stored after successful validation.
+	outputPath OutputPath
+
+	// Map of all package modules we visit during GenerateBuildActions
+	packages map[string]packageProperties
+	// Map of all team modules we visit during GenerateBuildActions
+	teams map[string]teamProperties
+	// Keeps track of team information or bp file for each module we visit.
+	teams_for_mods map[string]moduleTeamInfo
+}
+
+// See if there is a package module for the given bpFilePath with a team defined, if so return the team.
+// If not ascend up to the parent directory and do the same.
+func (this *allTeamsSingleton) lookupDefaultTeam(bpFilePath string) (teamProperties, bool) {
+	// return the Default_team listed in the package if is there.
+	if p, ok := this.packages[bpFilePath]; ok {
+		if t := p.Default_team; t != nil {
+			return this.teams[*p.Default_team], true
+		}
+	}
+	// Strip a directory and go up.
+	// Does android/paths.go basePath,SourcePath help?
+	current, base := filepath.Split(bpFilePath)
+	current = filepath.Clean(current) // removes trailing slash, convert "" -> "."
+	parent, _ := filepath.Split(current)
+	if current == "." {
+		return teamProperties{}, false
+	}
+	return this.lookupDefaultTeam(filepath.Join(parent, base))
+}
+
+// Create a rule to run a tool to collect all the intermediate files
+// which list the team per module into one proto file.
+func (this *allTeamsSingleton) GenerateBuildActions(ctx SingletonContext) {
+	this.packages = make(map[string]packageProperties)
+	this.teams = make(map[string]teamProperties)
+	this.teams_for_mods = make(map[string]moduleTeamInfo)
+
+	ctx.VisitAllModules(func(module Module) {
+		if !module.Enabled() {
+			return
+		}
+
+		bpFile := ctx.BlueprintFile(module)
+
+		// Package Modules and Team Modules are stored in a map so we can look them up by name for
+		// modules without a team.
+		if pack, ok := module.(*packageModule); ok {
+			// Packages don't have names, use the blueprint file as the key. we can't get qualifiedModuleId in this context.
+			pkgKey := bpFile
+			this.packages[pkgKey] = pack.properties
+			return
+		}
+		if team, ok := module.(*teamModule); ok {
+			this.teams[team.Name()] = team.properties
+			return
+		}
+
+		// If a team name is given for a module, store it.
+		// Otherwise store the bpFile so we can do a package walk later.
+		if module.base().Team() != "" {
+			this.teams_for_mods[module.Name()] = moduleTeamInfo{teamName: module.base().Team(), bpFile: bpFile}
+		} else {
+			this.teams_for_mods[module.Name()] = moduleTeamInfo{bpFile: bpFile}
+		}
+	})
+
+	// Visit all modules again and lookup the team name in the package or parent package if the team
+	// isn't assignged at the module level.
+	allTeams := this.lookupTeamForAllModules()
+
+	this.outputPath = PathForOutput(ctx, ownershipDirectory, allTeamsFile)
+	// udc branch diff, use textproto and encode it.
+	data, err := prototext.Marshal(allTeams)
+	if err != nil {
+		ctx.Errorf("Unable to marshal team data. %s", err)
+	}
+
+	WriteFileRuleVerbatim(ctx, this.outputPath, proptools.NinjaEscape(string(data)))
+	ctx.Phony("all_teams", this.outputPath)
+}
+
+func (this *allTeamsSingleton) MakeVars(ctx MakeVarsContext) {
+	ctx.DistForGoal("all_teams", this.outputPath)
+}
+
+// Visit every (non-package, non-team) module and write out a proto containing
+// either the declared team data for that module or the package default team data for that module.
+func (this *allTeamsSingleton) lookupTeamForAllModules() *team_proto.AllTeams {
+	teamsProto := make([]*team_proto.Team, len(this.teams_for_mods))
+	i := 0
+	for moduleName, m := range this.teams_for_mods {
+		teamName := m.teamName
+		var teamProperties teamProperties
+		found := false
+		if teamName != "" {
+			teamProperties, found = this.teams[teamName]
+		} else {
+			teamProperties, found = this.lookupDefaultTeam(m.bpFile)
+		}
+
+		// udc branch diff, only write modules with teams to keep ninja file smaller.
+		if found {
+			trendy_team_id := *teamProperties.Trendy_team_id
+			var files []string
+			teamData := new(team_proto.Team)
+			*teamData = team_proto.Team{
+				TrendyTeamId: proto.String(trendy_team_id),
+				TargetName:   proto.String(moduleName),
+				Path:         proto.String(m.bpFile),
+				File:         files,
+			}
+			teamsProto[i] = teamData
+			i++
+		}
+	}
+	return &team_proto.AllTeams{Teams: teamsProto[:i]}
+}
diff --git a/android/all_teams_test.go b/android/all_teams_test.go
new file mode 100644
index 0000000..6302d86
--- /dev/null
+++ b/android/all_teams_test.go
@@ -0,0 +1,208 @@
+// Copyright 2024 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//	http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package android
+
+import (
+	"android/soong/android/team_proto"
+	"log"
+	"testing"
+
+	"google.golang.org/protobuf/encoding/prototext"
+	"google.golang.org/protobuf/proto"
+)
+
+func TestAllTeams(t *testing.T) {
+	t.Parallel()
+	ctx := GroupFixturePreparers(
+		PrepareForTestWithTeamBuildComponents,
+		FixtureRegisterWithContext(func(ctx RegistrationContext) {
+			ctx.RegisterModuleType("fake", fakeModuleFactory)
+			ctx.RegisterSingletonType("all_teams", AllTeamsFactory)
+		}),
+	).RunTestWithBp(t, `
+		fake {
+			name: "main_test",
+			team: "someteam",
+		}
+		team {
+			name: "someteam",
+			trendy_team_id: "cool_team",
+		}
+
+		team {
+			name: "team2",
+			trendy_team_id: "22222",
+		}
+
+		fake {
+			name: "tool",
+			team: "team2",
+		}
+
+		fake {
+			name: "noteam",
+		}
+	`)
+
+	var teams *team_proto.AllTeams
+	teams = getTeamProtoOutput(t, ctx)
+
+	// map of module name -> trendy team name.
+	actualTeams := make(map[string]*string)
+	for _, teamProto := range teams.Teams {
+		actualTeams[teamProto.GetTargetName()] = teamProto.TrendyTeamId
+	}
+	expectedTeams := map[string]*string{
+		"main_test": proto.String("cool_team"),
+		"tool":      proto.String("22222"),
+	}
+
+	AssertDeepEquals(t, "compare maps", expectedTeams, actualTeams)
+}
+
+func getTeamProtoOutput(t *testing.T, ctx *TestResult) *team_proto.AllTeams {
+	teams := new(team_proto.AllTeams)
+	config := ctx.SingletonForTests("all_teams")
+	allOutputs := config.AllOutputs()
+
+	protoPath := allOutputs[0]
+
+	out := config.MaybeOutput(protoPath)
+	outProto := []byte(ContentFromFileRuleForTests(t, out))
+	// udc diff
+	if err := prototext.Unmarshal(outProto, teams); err != nil {
+		log.Fatalln("Failed to parse teams proto:", err)
+	}
+	return teams
+}
+
+// Android.bp
+//
+//	team: team_top
+//
+// # dir1 has no modules with teams,
+// # but has a dir with no Android.bp
+// dir1/Android.bp
+//
+//	module_dir1
+//
+// # dirs without and Android.bp should be fine.
+// dir1/dir2/dir3/Android.bp
+//
+//	package {}
+//	module_dir123
+//
+// teams_dir/Android.bp
+//
+//	module_with_team1: team1
+//	team1: 111
+//
+// # team comes from upper package default
+// teams_dir/deeper/Android.bp
+//
+//	module2_with_team1: team1
+//
+// package_defaults/Android.bp
+// package_defaults/pd2/Android.bp
+//
+//	package{ default_team: team_top}
+//	module_pd2   ## should get team_top
+//
+// package_defaults/pd2/pd3/Android.bp
+//
+//	module_pd3  ## should get team_top
+func TestPackageLookup(t *testing.T) {
+	t.Parallel()
+	rootBp := `
+		team {
+			name: "team_top",
+			trendy_team_id: "trendy://team_top",
+		} `
+
+	dir1Bp := `
+		fake {
+			name: "module_dir1",
+		} `
+	dir3Bp := `
+                package {}
+		fake {
+			name: "module_dir123",
+		} `
+	teamsDirBp := `
+		fake {
+			name: "module_with_team1",
+                        team: "team1"
+
+		}
+		team {
+			name: "team1",
+			trendy_team_id: "111",
+		} `
+	teamsDirDeeper := `
+		fake {
+			name: "module2_with_team1",
+                        team: "team1"
+		} `
+	// create an empty one.
+	packageDefaultsBp := ""
+	packageDefaultspd2 := `
+                package { default_team: "team_top"}
+		fake {
+			name: "modulepd2",
+		} `
+
+	packageDefaultspd3 := `
+		fake {
+			name: "modulepd3",
+		}
+		fake {
+			name: "modulepd3b",
+			team: "team1"
+		} `
+
+	ctx := GroupFixturePreparers(
+		PrepareForTestWithTeamBuildComponents,
+		PrepareForTestWithPackageModule,
+		FixtureRegisterWithContext(func(ctx RegistrationContext) {
+			ctx.RegisterModuleType("fake", fakeModuleFactory)
+			ctx.RegisterSingletonType("all_teams", AllTeamsFactory)
+		}),
+		FixtureAddTextFile("Android.bp", rootBp),
+		FixtureAddTextFile("dir1/Android.bp", dir1Bp),
+		FixtureAddTextFile("dir1/dir2/dir3/Android.bp", dir3Bp),
+		FixtureAddTextFile("teams_dir/Android.bp", teamsDirBp),
+		FixtureAddTextFile("teams_dir/deeper/Android.bp", teamsDirDeeper),
+		FixtureAddTextFile("package_defaults/Android.bp", packageDefaultsBp),
+		FixtureAddTextFile("package_defaults/pd2/Android.bp", packageDefaultspd2),
+		FixtureAddTextFile("package_defaults/pd2/pd3/Android.bp", packageDefaultspd3),
+	).RunTest(t)
+
+	var teams *team_proto.AllTeams
+	teams = getTeamProtoOutput(t, ctx)
+
+	// map of module name -> trendy team name.
+	actualTeams := make(map[string]*string)
+	for _, teamProto := range teams.Teams {
+		actualTeams[teamProto.GetTargetName()] = teamProto.TrendyTeamId
+	}
+	expectedTeams := map[string]*string{
+		"module_with_team1":  proto.String("111"),
+		"module2_with_team1": proto.String("111"),
+		"modulepd2":          proto.String("trendy://team_top"),
+		"modulepd3":          proto.String("trendy://team_top"),
+		"modulepd3b":         proto.String("111"),
+		// udc diff, no nil teams
+	}
+	AssertDeepEquals(t, "compare maps", expectedTeams, actualTeams)
+}
diff --git a/android/module.go b/android/module.go
index 76b4e3d..05f7682 100644
--- a/android/module.go
+++ b/android/module.go
@@ -953,6 +953,10 @@
 
 	// Bazel conversion status
 	BazelConversionStatus BazelConversionStatus `blueprint:"mutated"`
+
+	// The team (defined by the owner/vendor) who owns the property.
+	Team *string `android:"path"`
+
 }
 
 // CommonAttributes represents the common Bazel attributes from which properties
@@ -995,6 +999,12 @@
 	Dists []Dist `android:"arch_variant"`
 }
 
+type TeamDepTagType struct {
+	blueprint.BaseDependencyTag
+}
+
+var teamDepTag = TeamDepTagType{}
+
 // CommonTestOptions represents the common `test_options` properties in
 // Android.bp.
 type CommonTestOptions struct {
@@ -1699,6 +1709,12 @@
 
 func (m *ModuleBase) DepsMutator(BottomUpMutatorContext) {}
 
+func (m *ModuleBase) baseDepsMutator(ctx BottomUpMutatorContext) {
+	if m.Team() != "" {
+		ctx.AddDependency(ctx.Module(), teamDepTag, m.Team())
+	}
+}
+
 // AddProperties "registers" the provided props
 // each value in props MUST be a pointer to a struct
 func (m *ModuleBase) AddProperties(props ...interface{}) {
@@ -2136,6 +2152,10 @@
 	return String(m.commonProperties.Owner)
 }
 
+func (m *ModuleBase) Team() string {
+	return String(m.commonProperties.Team)
+}
+
 func (m *ModuleBase) setImageVariation(variant string) {
 	m.commonProperties.ImageVariation = variant
 }
diff --git a/android/mutator.go b/android/mutator.go
index 013fa77..0afa767 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -681,6 +681,7 @@
 
 func depsMutator(ctx BottomUpMutatorContext) {
 	if m := ctx.Module(); m.Enabled() {
+		m.base().baseDepsMutator(ctx)
 		m.DepsMutator(ctx)
 	}
 }
diff --git a/android/package.go b/android/package.go
index 7fbc700..682b350 100644
--- a/android/package.go
+++ b/android/package.go
@@ -38,6 +38,7 @@
 	Default_visibility []string
 	// Specifies the default license terms for all modules defined in this package.
 	Default_applicable_licenses []string
+	Default_team                *string `android:"path"`
 }
 
 type bazelPackageAttributes struct {
@@ -90,6 +91,13 @@
 	// Nothing to do.
 }
 
+func (p *packageModule) DepsMutator(ctx BottomUpMutatorContext) {
+	// Add the dependency to do a validity check
+	if p.properties.Default_team != nil {
+		ctx.AddDependency(ctx.Module(), nil, *p.properties.Default_team)
+	}
+}
+
 func (p *packageModule) GenerateBuildActions(ctx blueprint.ModuleContext) {
 	// Nothing to do.
 }
diff --git a/android/team.go b/android/team.go
new file mode 100644
index 0000000..df61f40
--- /dev/null
+++ b/android/team.go
@@ -0,0 +1,58 @@
+// 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 android
+
+func init() {
+	RegisterTeamBuildComponents(InitRegistrationContext)
+}
+
+func RegisterTeamBuildComponents(ctx RegistrationContext) {
+	ctx.RegisterModuleType("team", TeamFactory)
+}
+
+var PrepareForTestWithTeamBuildComponents = GroupFixturePreparers(
+	FixtureRegisterWithContext(RegisterTeamBuildComponents),
+)
+
+type teamProperties struct {
+	Trendy_team_id *string `json:"trendy_team_id"`
+}
+
+type teamModule struct {
+	ModuleBase
+	DefaultableModuleBase
+
+	properties teamProperties
+}
+
+// Real work is done for the module that depends on us.
+// If needed, the team can serialize the config to json/proto file as well.
+func (t *teamModule) GenerateAndroidBuildActions(ctx ModuleContext) {}
+
+func (t *teamModule) TrendyTeamId(ctx ModuleContext) string {
+	return *t.properties.Trendy_team_id
+}
+
+func TeamFactory() Module {
+	module := &teamModule{}
+
+	base := module.base()
+	module.AddProperties(&base.nameProperties, &module.properties)
+
+	InitAndroidModule(module)
+	InitDefaultableModule(module)
+
+	return module
+}
diff --git a/android/team_proto/Android.bp b/android/team_proto/Android.bp
new file mode 100644
index 0000000..061e77e
--- /dev/null
+++ b/android/team_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-android_team_proto",
+    pkgPath: "android/soong/android/team_proto",
+    deps: [
+        "golang-protobuf-reflect-protoreflect",
+        "golang-protobuf-runtime-protoimpl",
+    ],
+    srcs: [
+        "team.pb.go",
+    ],
+}
diff --git a/android/team_proto/OWNERS b/android/team_proto/OWNERS
new file mode 100644
index 0000000..2beb4f4
--- /dev/null
+++ b/android/team_proto/OWNERS
@@ -0,0 +1,5 @@
+dariofreni@google.com
+joeo@google.com
+ronish@google.com
+caditya@google.com
+rbraunstein@google.com
diff --git a/android/team_proto/regen.sh b/android/team_proto/regen.sh
new file mode 100755
index 0000000..63b2016
--- /dev/null
+++ b/android/team_proto/regen.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+aprotoc --go_out=paths=source_relative:. team.proto
diff --git a/android/team_proto/team.pb.go b/android/team_proto/team.pb.go
new file mode 100644
index 0000000..61260cf
--- /dev/null
+++ b/android/team_proto/team.pb.go
@@ -0,0 +1,253 @@
+// 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: team.proto
+
+package team_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 Team 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: Files directly owned by this module.
+	File []string `protobuf:"bytes,4,rep,name=file" json:"file,omitempty"`
+}
+
+func (x *Team) Reset() {
+	*x = Team{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_team_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *Team) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Team) ProtoMessage() {}
+
+func (x *Team) ProtoReflect() protoreflect.Message {
+	mi := &file_team_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 Team.ProtoReflect.Descriptor instead.
+func (*Team) Descriptor() ([]byte, []int) {
+	return file_team_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *Team) GetTargetName() string {
+	if x != nil && x.TargetName != nil {
+		return *x.TargetName
+	}
+	return ""
+}
+
+func (x *Team) GetPath() string {
+	if x != nil && x.Path != nil {
+		return *x.Path
+	}
+	return ""
+}
+
+func (x *Team) GetTrendyTeamId() string {
+	if x != nil && x.TrendyTeamId != nil {
+		return *x.TrendyTeamId
+	}
+	return ""
+}
+
+func (x *Team) GetFile() []string {
+	if x != nil {
+		return x.File
+	}
+	return nil
+}
+
+type AllTeams struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Teams []*Team `protobuf:"bytes,1,rep,name=teams" json:"teams,omitempty"`
+}
+
+func (x *AllTeams) Reset() {
+	*x = AllTeams{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_team_proto_msgTypes[1]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *AllTeams) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*AllTeams) ProtoMessage() {}
+
+func (x *AllTeams) ProtoReflect() protoreflect.Message {
+	mi := &file_team_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 AllTeams.ProtoReflect.Descriptor instead.
+func (*AllTeams) Descriptor() ([]byte, []int) {
+	return file_team_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *AllTeams) GetTeams() []*Team {
+	if x != nil {
+		return x.Teams
+	}
+	return nil
+}
+
+var File_team_proto protoreflect.FileDescriptor
+
+var file_team_proto_rawDesc = []byte{
+	0x0a, 0x0a, 0x74, 0x65, 0x61, 0x6d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0a, 0x74, 0x65,
+	0x61, 0x6d, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x75, 0x0a, 0x04, 0x54, 0x65, 0x61, 0x6d,
+	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, 0x12, 0x0a, 0x04, 0x66,
+	0x69, 0x6c, 0x65, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x22,
+	0x32, 0x0a, 0x08, 0x41, 0x6c, 0x6c, 0x54, 0x65, 0x61, 0x6d, 0x73, 0x12, 0x26, 0x0a, 0x05, 0x74,
+	0x65, 0x61, 0x6d, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x74, 0x65, 0x61,
+	0x6d, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x54, 0x65, 0x61, 0x6d, 0x52, 0x05, 0x74, 0x65,
+	0x61, 0x6d, 0x73, 0x42, 0x22, 0x5a, 0x20, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73,
+	0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x74, 0x65, 0x61,
+	0x6d, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+}
+
+var (
+	file_team_proto_rawDescOnce sync.Once
+	file_team_proto_rawDescData = file_team_proto_rawDesc
+)
+
+func file_team_proto_rawDescGZIP() []byte {
+	file_team_proto_rawDescOnce.Do(func() {
+		file_team_proto_rawDescData = protoimpl.X.CompressGZIP(file_team_proto_rawDescData)
+	})
+	return file_team_proto_rawDescData
+}
+
+var file_team_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
+var file_team_proto_goTypes = []interface{}{
+	(*Team)(nil),     // 0: team_proto.Team
+	(*AllTeams)(nil), // 1: team_proto.AllTeams
+}
+var file_team_proto_depIdxs = []int32{
+	0, // 0: team_proto.AllTeams.teams:type_name -> team_proto.Team
+	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_team_proto_init() }
+func file_team_proto_init() {
+	if File_team_proto != nil {
+		return
+	}
+	if !protoimpl.UnsafeEnabled {
+		file_team_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*Team); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_team_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*AllTeams); 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_team_proto_rawDesc,
+			NumEnums:      0,
+			NumMessages:   2,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_team_proto_goTypes,
+		DependencyIndexes: file_team_proto_depIdxs,
+		MessageInfos:      file_team_proto_msgTypes,
+	}.Build()
+	File_team_proto = out.File
+	file_team_proto_rawDesc = nil
+	file_team_proto_goTypes = nil
+	file_team_proto_depIdxs = nil
+}
diff --git a/android/team_proto/team.proto b/android/team_proto/team.proto
new file mode 100644
index 0000000..401eccc
--- /dev/null
+++ b/android/team_proto/team.proto
@@ -0,0 +1,34 @@
+// 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 team_proto;
+option go_package = "android/soong/android/team_proto";
+
+message Team {
+  // 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: Files directly owned by this module.
+  repeated string file = 4;
+}
+
+message AllTeams {
+  repeated Team teams = 1;
+}
diff --git a/android/team_test.go b/android/team_test.go
new file mode 100644
index 0000000..75b3e9f
--- /dev/null
+++ b/android/team_test.go
@@ -0,0 +1,99 @@
+// Copyright 2024 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//	http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package android
+
+import (
+	"testing"
+)
+
+type fakeModuleForTests struct {
+	ModuleBase
+}
+
+func fakeModuleFactory() Module {
+	module := &fakeModuleForTests{}
+	InitAndroidModule(module)
+	return module
+}
+
+func (*fakeModuleForTests) GenerateAndroidBuildActions(ModuleContext) {}
+
+func TestTeam(t *testing.T) {
+	t.Parallel()
+	ctx := GroupFixturePreparers(
+		PrepareForTestWithTeamBuildComponents,
+		FixtureRegisterWithContext(func(ctx RegistrationContext) {
+			ctx.RegisterModuleType("fake", fakeModuleFactory)
+		}),
+	).RunTestWithBp(t, `
+		fake {
+			name: "main_test",
+			team: "someteam",
+		}
+		team {
+			name: "someteam",
+			trendy_team_id: "cool_team",
+		}
+
+		team {
+			name: "team2",
+			trendy_team_id: "22222",
+		}
+
+		fake {
+			name: "tool",
+			team: "team2",
+		}
+	`)
+
+	// Assert the rule from GenerateAndroidBuildActions exists.
+	m := ctx.ModuleForTests("main_test", "")
+	AssertStringEquals(t, "msg", m.Module().base().Team(), "someteam")
+	m = ctx.ModuleForTests("tool", "")
+	AssertStringEquals(t, "msg", m.Module().base().Team(), "team2")
+}
+
+func TestMissingTeamFails(t *testing.T) {
+	t.Parallel()
+	GroupFixturePreparers(
+		PrepareForTestWithTeamBuildComponents,
+		FixtureRegisterWithContext(func(ctx RegistrationContext) {
+			ctx.RegisterModuleType("fake", fakeModuleFactory)
+		}),
+	).
+		ExtendWithErrorHandler(FixtureExpectsAtLeastOneErrorMatchingPattern("depends on undefined module \"ring-bearer")).
+		RunTestWithBp(t, `
+		fake {
+			name: "you_cannot_pass",
+			team: "ring-bearer",
+		}
+	`)
+}
+
+func TestPackageBadTeamNameFails(t *testing.T) {
+	t.Parallel()
+	GroupFixturePreparers(
+		PrepareForTestWithTeamBuildComponents,
+		PrepareForTestWithPackageModule,
+		FixtureRegisterWithContext(func(ctx RegistrationContext) {
+			ctx.RegisterModuleType("fake", fakeModuleFactory)
+		}),
+	).
+		ExtendWithErrorHandler(FixtureExpectsAtLeastOneErrorMatchingPattern("depends on undefined module \"ring-bearer")).
+		RunTestWithBp(t, `
+		package {
+			default_team: "ring-bearer",
+		}
+	`)
+}