Create (API) bp2build converters for droidstubs

- The converter runs for api_bp2build, not bp2build workspace
- Since droidstubs is an internal module created by java_sdk_library,
  the conversion encompasses the latter as well
- Since droidstubs do not have an api_surface attribute, this conversion
  uses naming convention to infer the api_surface represented by the api
  file e.g. *stubs.source -> publicapi, *stubs.source.system -> systemapi)
- Also adds an SdkIntraCore enum to represent the API surface provided
  by one core module to another

There is also ongoing work to check in java_api_contribution modules in
Soong. Once we have that, we can update this converter to operate on
that module type instead

Test: go test ./bp2build
Change-Id: Ia85828e04c738d9ffcc524856d7c3034ee29bbf9
diff --git a/java/droidstubs.go b/java/droidstubs.go
index 5777b18..2ad2969 100644
--- a/java/droidstubs.go
+++ b/java/droidstubs.go
@@ -18,11 +18,13 @@
 	"fmt"
 	"path/filepath"
 	"regexp"
+	"sort"
 	"strings"
 
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
+	"android/soong/bazel"
 	"android/soong/java/config"
 	"android/soong/remoteexec"
 )
@@ -834,6 +836,74 @@
 	}
 }
 
+var _ android.ApiProvider = (*Droidstubs)(nil)
+
+type bazelJavaApiContributionAttributes struct {
+	Api         bazel.LabelAttribute
+	Api_surface *string
+}
+
+func (d *Droidstubs) ConvertWithApiBp2build(ctx android.TopDownMutatorContext) {
+	props := bazel.BazelTargetModuleProperties{
+		Rule_class:        "java_api_contribution",
+		Bzl_load_location: "//build/bazel/rules/apis:java_api_contribution.bzl",
+	}
+	apiFile := d.properties.Check_api.Current.Api_file
+	// Do not generate a target if check_api is not set
+	if apiFile == nil {
+		return
+	}
+	attrs := &bazelJavaApiContributionAttributes{
+		Api: *bazel.MakeLabelAttribute(
+			android.BazelLabelForModuleSrcSingle(ctx, proptools.String(apiFile)).Label,
+		),
+		Api_surface: proptools.StringPtr(bazelApiSurfaceName(d.Name())),
+	}
+	ctx.CreateBazelTargetModule(props, android.CommonAttributes{
+		Name: android.ApiContributionTargetName(ctx.ModuleName()),
+	}, attrs)
+}
+
+// TODO (b/262014796): Export the API contributions of CorePlatformApi
+// A map to populate the api surface of a droidstub from a substring appearing in its name
+// This map assumes that droidstubs (either checked-in or created by java_sdk_library)
+// use a strict naming convention
+var (
+	droidstubsModuleNamingToSdkKind = map[string]android.SdkKind{
+		//public is commented out since the core libraries use public in their java_sdk_library names
+		"intracore":     android.SdkIntraCore,
+		"intra.core":    android.SdkIntraCore,
+		"system_server": android.SdkSystemServer,
+		"system-server": android.SdkSystemServer,
+		"system":        android.SdkSystem,
+		"module_lib":    android.SdkModule,
+		"module-lib":    android.SdkModule,
+		"test":          android.SdkTest,
+	}
+)
+
+// A helper function that returns the api surface of the corresponding java_api_contribution Bazel target
+// The api_surface is populated using the naming convention of the droidstubs module.
+func bazelApiSurfaceName(name string) string {
+	// Sort the keys so that longer strings appear first
+	// Otherwise substrings like system will match both system and system_server
+	sortedKeys := make([]string, 0)
+	for key := range droidstubsModuleNamingToSdkKind {
+		sortedKeys = append(sortedKeys, key)
+	}
+	sort.Slice(sortedKeys, func(i, j int) bool {
+		return len(sortedKeys[i]) > len(sortedKeys[j])
+	})
+	for _, sortedKey := range sortedKeys {
+		if strings.Contains(name, sortedKey) {
+			sdkKind := droidstubsModuleNamingToSdkKind[sortedKey]
+			return sdkKind.String() + "api"
+		}
+	}
+	// Default is publicapi
+	return android.SdkPublic.String() + "api"
+}
+
 func StubsDefaultsFactory() android.Module {
 	module := &DocDefaults{}
 
diff --git a/java/droidstubs_test.go b/java/droidstubs_test.go
index 25f8c86..ef2e6dc 100644
--- a/java/droidstubs_test.go
+++ b/java/droidstubs_test.go
@@ -304,3 +304,45 @@
 	android.AssertStringDoesContain(t, "sdk-extensions-root present", cmdline, "--sdk-extensions-root sdk/extensions")
 	android.AssertStringDoesContain(t, "sdk-extensions-info present", cmdline, "--sdk-extensions-info sdk/extensions/info.txt")
 }
+
+func TestApiSurfaceFromDroidStubsName(t *testing.T) {
+	testCases := []struct {
+		desc               string
+		name               string
+		expectedApiSurface string
+	}{
+		{
+			desc:               "Default is publicapi",
+			name:               "mydroidstubs",
+			expectedApiSurface: "publicapi",
+		},
+		{
+			desc:               "name contains system substring",
+			name:               "mydroidstubs.system.suffix",
+			expectedApiSurface: "systemapi",
+		},
+		{
+			desc:               "name contains system_server substring",
+			name:               "mydroidstubs.system_server.suffix",
+			expectedApiSurface: "system-serverapi",
+		},
+		{
+			desc:               "name contains module_lib substring",
+			name:               "mydroidstubs.module_lib.suffix",
+			expectedApiSurface: "module-libapi",
+		},
+		{
+			desc:               "name contains test substring",
+			name:               "mydroidstubs.test.suffix",
+			expectedApiSurface: "testapi",
+		},
+		{
+			desc:               "name contains intra.core substring",
+			name:               "mydroidstubs.intra.core.suffix",
+			expectedApiSurface: "intracoreapi",
+		},
+	}
+	for _, tc := range testCases {
+		android.AssertStringEquals(t, tc.desc, tc.expectedApiSurface, bazelApiSurfaceName(tc.name))
+	}
+}