java_sdk_library supports test as another API scope

For each java_sdk_library module, three (possibly different) stubs libs
are created.

*.stubs: public APIs only
*.system.stubs: public APIs + @SystemApi
*.test.stubs: public APIs + @TestApi

Depending on the sdk_version (or LOCAL_SDK_VERSION) of the calling
module, stubs lib of the corresponding api scope is linked.

Bug: 77575606
Test: m -j
Change-Id: I1437c64460914dbfc349dbc31d6f3ef090f541e3
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 2396467..45a6a09 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -31,6 +31,7 @@
 var (
 	sdkStubsLibrarySuffix = ".stubs"
 	sdkSystemApiSuffix    = ".system"
+	sdkTestApiSuffix      = ".test"
 	sdkDocsSuffix         = ".docs"
 	sdkImplLibrarySuffix  = ".impl"
 	sdkXmlFileSuffix      = ".xml"
@@ -44,6 +45,15 @@
 var (
 	publicApiStubsTag = dependencyTag{name: "public"}
 	systemApiStubsTag = dependencyTag{name: "system"}
+	testApiStubsTag   = dependencyTag{name: "test"}
+)
+
+type apiScope int
+
+const (
+	apiScopePublic apiScope = iota
+	apiScopeSystem
+	apiScopeTest
 )
 
 var (
@@ -104,12 +114,14 @@
 
 	publicApiStubsPath android.Paths
 	systemApiStubsPath android.Paths
+	testApiStubsPath   android.Paths
 }
 
 func (module *sdkLibrary) DepsMutator(ctx android.BottomUpMutatorContext) {
 	// Add dependencies to the stubs library
-	ctx.AddDependency(ctx.Module(), publicApiStubsTag, module.stubsName(false))
-	ctx.AddDependency(ctx.Module(), systemApiStubsTag, module.stubsName(true))
+	ctx.AddDependency(ctx.Module(), publicApiStubsTag, module.stubsName(apiScopePublic))
+	ctx.AddDependency(ctx.Module(), systemApiStubsTag, module.stubsName(apiScopeSystem))
+	ctx.AddDependency(ctx.Module(), testApiStubsTag, module.stubsName(apiScopeTest))
 }
 
 func (module *sdkLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
@@ -126,6 +138,8 @@
 				module.publicApiStubsPath = stubs.HeaderJars()
 			case systemApiStubsTag:
 				module.systemApiStubsPath = stubs.HeaderJars()
+			case testApiStubsTag:
+				module.testApiStubsPath = stubs.HeaderJars()
 			default:
 				ctx.ModuleErrorf("depends on module %q of unknown tag %q", otherName, tag)
 			}
@@ -148,19 +162,25 @@
 }
 
 // Module name of the stubs library
-func (module *sdkLibrary) stubsName(forSystemApi bool) string {
+func (module *sdkLibrary) stubsName(apiScope apiScope) string {
 	stubsName := module.BaseModuleName() + sdkStubsLibrarySuffix
-	if forSystemApi {
+	switch apiScope {
+	case apiScopeSystem:
 		stubsName = stubsName + sdkSystemApiSuffix
+	case apiScopeTest:
+		stubsName = stubsName + sdkTestApiSuffix
 	}
 	return stubsName
 }
 
 // Module name of the docs
-func (module *sdkLibrary) docsName(forSystemApi bool) string {
+func (module *sdkLibrary) docsName(apiScope apiScope) string {
 	docsName := module.BaseModuleName() + sdkDocsSuffix
-	if forSystemApi {
+	switch apiScope {
+	case apiScopeSystem:
 		docsName = docsName + sdkSystemApiSuffix
+	case apiScopeTest:
+		docsName = docsName + sdkTestApiSuffix
 	}
 	return docsName
 }
@@ -191,10 +211,15 @@
 // SDK version that the stubs library is built against. Note that this is always
 // *current. Older stubs library built with a numberd SDK version is created from
 // the prebuilt jar.
-func (module *sdkLibrary) sdkVersion(forSystemApi bool) string {
-	if forSystemApi {
+func (module *sdkLibrary) sdkVersion(apiScope apiScope) string {
+	switch apiScope {
+	case apiScopePublic:
+		return "current"
+	case apiScopeSystem:
 		return "system_current"
-	} else {
+	case apiScopeTest:
+		return "test_current"
+	default:
 		return "current"
 	}
 }
@@ -202,10 +227,13 @@
 // $(INTERNAL_PLATFORM_<apiTagName>_API_FILE) points to the generated
 // api file for the current source
 // TODO: remove this when apicheck is done in soong
-func (module *sdkLibrary) apiTagName(forSystemApi bool) string {
+func (module *sdkLibrary) apiTagName(apiScope apiScope) string {
 	apiTagName := strings.Replace(strings.ToUpper(module.BaseModuleName()), ".", "_", -1)
-	if forSystemApi {
+	switch apiScope {
+	case apiScopeSystem:
 		apiTagName = apiTagName + "_SYSTEM"
+	case apiScopeTest:
+		apiTagName = apiTagName + "_TEST"
 	}
 	return apiTagName
 }
@@ -213,11 +241,14 @@
 // returns the path (relative to this module) to the API txt file. Files are located
 // ./<api_dir>/<api_level>.txt where <api_level> is either current, system-current, removed,
 // or system-removed.
-func (module *sdkLibrary) apiFilePath(apiLevel string, forSystemApi bool) string {
+func (module *sdkLibrary) apiFilePath(apiLevel string, apiScope apiScope) string {
 	apiDir := "api"
 	apiFile := apiLevel
-	if forSystemApi {
+	switch apiScope {
+	case apiScopeSystem:
 		apiFile = "system-" + apiFile
+	case apiScopeTest:
+		apiFile = "test-" + apiFile
 	}
 	apiFile = apiFile + ".txt"
 
@@ -225,7 +256,7 @@
 }
 
 // Creates a static java library that has API stubs
-func (module *sdkLibrary) createStubsLibrary(mctx android.TopDownMutatorContext, forSystemApi bool) {
+func (module *sdkLibrary) createStubsLibrary(mctx android.TopDownMutatorContext, apiScope apiScope) {
 	props := struct {
 		Name              *string
 		Srcs              []string
@@ -243,10 +274,10 @@
 		}
 	}{}
 
-	props.Name = proptools.StringPtr(module.stubsName(forSystemApi))
+	props.Name = proptools.StringPtr(module.stubsName(apiScope))
 	// sources are generated from the droiddoc
-	props.Srcs = []string{":" + module.docsName(forSystemApi)}
-	props.Sdk_version = proptools.StringPtr(module.sdkVersion(forSystemApi))
+	props.Srcs = []string{":" + module.docsName(apiScope)}
+	props.Sdk_version = proptools.StringPtr(module.sdkVersion(apiScope))
 	// Unbundled apps will use the prebult one from /prebuilts/sdk
 	props.Product_variables.Unbundled_build.Enabled = proptools.BoolPtr(false)
 	props.Product_variables.Pdk.Enabled = proptools.BoolPtr(false)
@@ -264,7 +295,7 @@
 
 // Creates a droiddoc module that creates stubs source files from the given full source
 // files
-func (module *sdkLibrary) createDocs(mctx android.TopDownMutatorContext, forSystemApi bool) {
+func (module *sdkLibrary) createDocs(mctx android.TopDownMutatorContext, apiScope apiScope) {
 	props := struct {
 		Name                    *string
 		Srcs                    []string
@@ -280,7 +311,7 @@
 		Removed_api_filename    *string
 	}{}
 
-	props.Name = proptools.StringPtr(module.docsName(forSystemApi))
+	props.Name = proptools.StringPtr(module.docsName(apiScope))
 	props.Srcs = module.properties.Srcs
 	props.Custom_template = proptools.StringPtr("droiddoc-templates-sdk")
 	props.Installable = proptools.BoolPtr(false)
@@ -289,8 +320,11 @@
 	droiddocArgs := " -hide 110 -hide 111 -hide 113 -hide 121 -hide 125 -hide 126 -hide 127 -hide 128" +
 		" -stubpackages " + strings.Join(module.properties.Api_packages, ":") +
 		" -nodocs"
-	if forSystemApi {
+	switch apiScope {
+	case apiScopeSystem:
 		droiddocArgs = droiddocArgs + " -showAnnotation android.annotation.SystemApi"
+	case apiScopeTest:
+		droiddocArgs = droiddocArgs + " -showAnnotation android.annotation.TestApi"
 	}
 	props.Args = proptools.StringPtr(droiddocArgs)
 
@@ -300,13 +334,17 @@
 	// TODO: If any incompatible change is detected, break the build
 	currentApiFileName := "current.txt"
 	removedApiFileName := "removed.txt"
-	if forSystemApi {
+	switch apiScope {
+	case apiScopeSystem:
 		currentApiFileName = "system-" + currentApiFileName
 		removedApiFileName = "system-" + removedApiFileName
+	case apiScopeTest:
+		currentApiFileName = "test-" + currentApiFileName
+		removedApiFileName = "test-" + removedApiFileName
 	}
 	currentApiFileName = path.Join("api", currentApiFileName)
 	removedApiFileName = path.Join("api", removedApiFileName)
-	props.Api_tag_name = proptools.StringPtr(module.apiTagName(forSystemApi))
+	props.Api_tag_name = proptools.StringPtr(module.apiTagName(apiScope))
 	// Note: the exact names of these two are not important because they are always
 	// referenced by the make variable $(INTERNAL_PLATFORM_<TAG_NAME>_API_FILE)
 	props.Api_filename = proptools.StringPtr(currentApiFileName)
@@ -448,12 +486,16 @@
 func sdkLibraryMutator(mctx android.TopDownMutatorContext) {
 	if module, ok := mctx.Module().(*sdkLibrary); ok {
 		// for public API stubs
-		module.createStubsLibrary(mctx, false)
-		module.createDocs(mctx, false)
+		module.createStubsLibrary(mctx, apiScopePublic)
+		module.createDocs(mctx, apiScopePublic)
 
 		// for system API stubs
-		module.createStubsLibrary(mctx, true)
-		module.createDocs(mctx, true)
+		module.createStubsLibrary(mctx, apiScopeSystem)
+		module.createDocs(mctx, apiScopeSystem)
+
+		// for test API stubs
+		module.createStubsLibrary(mctx, apiScopeTest)
+		module.createDocs(mctx, apiScopeTest)
 
 		// for runtime
 		module.createXmlFile(mctx)