java_sdk_library_import - expose system and test stubs

Previously, the java_sdk_library_import only exposed the public stubs.
This change adds support for exposing system and test stubs too by adding
separate structures for public, system and test scopes. The existing
properties are kept for legacy reasons (and because libs can be common
across the differents scopes).

It extracts some code that is common to both sdk library and sdk
library import.

The legacy support will be removed in a future change once all existing
usages have been switched over.

Bug: 148080325
Test: m droid
      TARGET_BUILD_APPS=Camera2 m
Change-Id: I0b26cc8af9ee044437ff3b80c1eca611816b9386
diff --git a/java/java_test.go b/java/java_test.go
index c4ab13d..924f76b 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -458,7 +458,7 @@
 		java_library {
 			name: "foo",
 			srcs: ["a.java", ":stubs-source"],
-			libs: ["bar", "sdklib"],
+			libs: ["bar", "sdklib", "sdklib-legacy"],
 			static_libs: ["baz"],
 		}
 
@@ -478,10 +478,17 @@
 		}
 
 		java_sdk_library_import {
-			name: "sdklib",
+			name: "sdklib-legacy",
 			jars: ["b.jar"],
 		}
 
+		java_sdk_library_import {
+			name: "sdklib",
+			public: {
+				jars: ["c.jar"],
+			},
+		}
+
 		prebuilt_stubs_sources {
 			name: "stubs-source",
 			srcs: ["stubs/sources"],
@@ -531,6 +538,54 @@
 	}
 }
 
+func TestJavaSdkLibraryImport(t *testing.T) {
+	ctx, _ := testJava(t, `
+		java_library {
+			name: "foo",
+			srcs: ["a.java"],
+			libs: ["sdklib"],
+			sdk_version: "current",
+		}
+
+		java_library {
+			name: "foo.system",
+			srcs: ["a.java"],
+			libs: ["sdklib"],
+			sdk_version: "system_current",
+		}
+
+		java_library {
+			name: "foo.test",
+			srcs: ["a.java"],
+			libs: ["sdklib"],
+			sdk_version: "test_current",
+		}
+
+		java_sdk_library_import {
+			name: "sdklib",
+			public: {
+				jars: ["a.jar"],
+			},
+			system: {
+				jars: ["b.jar"],
+			},
+			test: {
+				jars: ["c.jar"],
+			},
+		}
+		`)
+
+	for _, scope := range []string{"", ".system", ".test"} {
+		fooModule := ctx.ModuleForTests("foo"+scope, "android_common")
+		javac := fooModule.Rule("javac")
+
+		sdklibStubsJar := ctx.ModuleForTests("sdklib.stubs"+scope, "android_common").Rule("combineJar").Output
+		if !strings.Contains(javac.Args["classpath"], sdklibStubsJar.String()) {
+			t.Errorf("foo classpath %v does not contain %q", javac.Args["classpath"], sdklibStubsJar.String())
+		}
+	}
+}
+
 func TestDefaults(t *testing.T) {
 	ctx, _ := testJava(t, `
 		java_defaults {
diff --git a/java/sdk_library.go b/java/sdk_library.go
index d51d317..503f1d6 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -225,12 +225,30 @@
 	apiFilePath     android.Path
 }
 
+// Common code between sdk library and sdk library import
+type commonToSdkLibraryAndImport struct {
+	scopePaths map[*apiScope]*scopePaths
+}
+
+func (c *commonToSdkLibraryAndImport) getScopePaths(scope *apiScope) *scopePaths {
+	if c.scopePaths == nil {
+		c.scopePaths = make(map[*apiScope]*scopePaths)
+	}
+	paths := c.scopePaths[scope]
+	if paths == nil {
+		paths = &scopePaths{}
+		c.scopePaths[scope] = paths
+	}
+
+	return paths
+}
+
 type SdkLibrary struct {
 	Library
 
 	sdkLibraryProperties sdkLibraryProperties
 
-	scopePaths map[*apiScope]*scopePaths
+	commonToSdkLibraryAndImport
 
 	permissionsFile android.Path
 }
@@ -246,19 +264,6 @@
 	}
 }
 
-func (module *SdkLibrary) getScopePaths(scope *apiScope) *scopePaths {
-	if module.scopePaths == nil {
-		module.scopePaths = make(map[*apiScope]*scopePaths)
-	}
-	paths := module.scopePaths[scope]
-	if paths == nil {
-		paths = &scopePaths{}
-		module.scopePaths[scope] = paths
-	}
-
-	return paths
-}
-
 func (module *SdkLibrary) DepsMutator(ctx android.BottomUpMutatorContext) {
 	useBuiltStubs := !ctx.Config().UnbundledBuildUsePrebuiltSdks()
 	for _, apiScope := range module.getActiveApiScopes() {
@@ -833,7 +838,8 @@
 // SDK library prebuilts
 //
 
-type sdkLibraryImportProperties struct {
+// Properties associated with each api scope.
+type sdkLibraryScopeProperties struct {
 	Jars []string `android:"path"`
 
 	Sdk_version *string
@@ -842,6 +848,17 @@
 	Libs []string
 }
 
+type sdkLibraryImportProperties struct {
+	// Properties associated with the public api scope.
+	Public sdkLibraryScopeProperties
+
+	// Properties associated with the system api scope.
+	System sdkLibraryScopeProperties
+
+	// Properties associated with the test api scope.
+	Test sdkLibraryScopeProperties
+}
+
 type sdkLibraryImport struct {
 	android.ModuleBase
 	android.DefaultableModuleBase
@@ -849,7 +866,12 @@
 
 	properties sdkLibraryImportProperties
 
-	stubsPath android.Paths
+	// Legacy properties for the public api scope.
+	//
+	// Should use properties.Public instead.
+	legacyPublicProperties sdkLibraryScopeProperties
+
+	commonToSdkLibraryAndImport
 }
 
 var _ SdkLibraryDependency = (*sdkLibraryImport)(nil)
@@ -858,9 +880,9 @@
 func sdkLibraryImportFactory() android.Module {
 	module := &sdkLibraryImport{}
 
-	module.AddProperties(&module.properties)
+	module.AddProperties(&module.properties, &module.legacyPublicProperties)
 
-	android.InitPrebuiltModule(module, &module.properties.Jars)
+	android.InitPrebuiltModule(module, &module.legacyPublicProperties.Jars)
 	InitJavaModule(module, android.HostAndDeviceSupported)
 
 	android.AddLoadHook(module, func(mctx android.LoadHookContext) { module.createInternalModules(mctx) })
@@ -876,28 +898,57 @@
 }
 
 func (module *sdkLibraryImport) createInternalModules(mctx android.LoadHookContext) {
-	// Creates a java import for the jar with ".stubs" suffix
-	props := struct {
-		Name                *string
-		Soc_specific        *bool
-		Device_specific     *bool
-		Product_specific    *bool
-		System_ext_specific *bool
-	}{}
 
-	props.Name = proptools.StringPtr(module.BaseModuleName() + sdkStubsLibrarySuffix)
-
-	if module.SocSpecific() {
-		props.Soc_specific = proptools.BoolPtr(true)
-	} else if module.DeviceSpecific() {
-		props.Device_specific = proptools.BoolPtr(true)
-	} else if module.ProductSpecific() {
-		props.Product_specific = proptools.BoolPtr(true)
-	} else if module.SystemExtSpecific() {
-		props.System_ext_specific = proptools.BoolPtr(true)
+	// Prepend any of the libs from the legacy public properties to the libs for each of the
+	// scopes to avoid having to duplicate them in each scope.
+	for _, scopeProperties := range module.scopeProperties() {
+		scopeProperties.Libs = append(module.legacyPublicProperties.Libs, scopeProperties.Libs...)
 	}
 
-	mctx.CreateModule(ImportFactory, &props, &module.properties)
+	if module.legacyPublicProperties.Jars != nil {
+		if module.properties.Public.Jars != nil {
+			mctx.ModuleErrorf("cannot set both `jars` and `public.jars`")
+			return
+		}
+
+		// The legacy set of properties has been used so copy them over the public properties.
+		module.properties.Public = module.legacyPublicProperties
+	}
+
+	for apiScope, scopeProperties := range module.scopeProperties() {
+		if len(scopeProperties.Jars) == 0 {
+			continue
+		}
+
+		// Creates a java import for the jar with ".stubs" suffix
+		props := struct {
+			Name                *string
+			Soc_specific        *bool
+			Device_specific     *bool
+			Product_specific    *bool
+			System_ext_specific *bool
+			Sdk_version         *string
+			Libs                []string
+			Jars                []string
+		}{}
+
+		props.Name = proptools.StringPtr(apiScope.stubsModuleName(module.BaseModuleName()))
+		props.Sdk_version = scopeProperties.Sdk_version
+		props.Libs = scopeProperties.Libs
+		props.Jars = scopeProperties.Jars
+
+		if module.SocSpecific() {
+			props.Soc_specific = proptools.BoolPtr(true)
+		} else if module.DeviceSpecific() {
+			props.Device_specific = proptools.BoolPtr(true)
+		} else if module.ProductSpecific() {
+			props.Product_specific = proptools.BoolPtr(true)
+		} else if module.SystemExtSpecific() {
+			props.System_ext_specific = proptools.BoolPtr(true)
+		}
+
+		mctx.CreateModule(ImportFactory, &props)
+	}
 
 	javaSdkLibraries := javaSdkLibraries(mctx.Config())
 	javaSdkLibrariesLock.Lock()
@@ -905,9 +956,23 @@
 	*javaSdkLibraries = append(*javaSdkLibraries, module.BaseModuleName())
 }
 
+func (module *sdkLibraryImport) scopeProperties() map[*apiScope]*sdkLibraryScopeProperties {
+	p := make(map[*apiScope]*sdkLibraryScopeProperties)
+	p[apiScopePublic] = &module.properties.Public
+	p[apiScopeSystem] = &module.properties.System
+	p[apiScopeTest] = &module.properties.Test
+	return p
+}
+
 func (module *sdkLibraryImport) DepsMutator(ctx android.BottomUpMutatorContext) {
-	// Add dependencies to the prebuilt stubs library
-	ctx.AddVariationDependencies(nil, apiScopePublic.stubsTag, module.BaseModuleName()+sdkStubsLibrarySuffix)
+	for apiScope, scopeProperties := range module.scopeProperties() {
+		if len(scopeProperties.Jars) == 0 {
+			continue
+		}
+
+		// Add dependencies to the prebuilt stubs library
+		ctx.AddVariationDependencies(nil, apiScope.stubsTag, apiScope.stubsModuleName(module.BaseModuleName()))
+	}
 }
 
 func (module *sdkLibraryImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
@@ -915,21 +980,42 @@
 	ctx.VisitDirectDeps(func(to android.Module) {
 		tag := ctx.OtherModuleDependencyTag(to)
 
-		switch tag {
-		case apiScopePublic.stubsTag:
-			module.stubsPath = to.(Dependency).HeaderJars()
+		if lib, ok := to.(Dependency); ok {
+			if scopeTag, ok := tag.(scopeDependencyTag); ok {
+				apiScope := scopeTag.apiScope
+				scopePaths := module.getScopePaths(apiScope)
+				scopePaths.stubsHeaderPath = lib.HeaderJars()
+			}
 		}
 	})
 }
 
+func (module *sdkLibraryImport) sdkJars(
+	ctx android.BaseModuleContext,
+	sdkVersion sdkSpec) android.Paths {
+
+	var apiScope *apiScope
+	switch sdkVersion.kind {
+	case sdkSystem:
+		apiScope = apiScopeSystem
+	case sdkTest:
+		apiScope = apiScopeTest
+	default:
+		apiScope = apiScopePublic
+	}
+
+	paths := module.getScopePaths(apiScope)
+	return paths.stubsHeaderPath
+}
+
 // to satisfy SdkLibraryDependency interface
 func (module *sdkLibraryImport) SdkHeaderJars(ctx android.BaseModuleContext, sdkVersion sdkSpec) android.Paths {
 	// This module is just a wrapper for the prebuilt stubs.
-	return module.stubsPath
+	return module.sdkJars(ctx, sdkVersion)
 }
 
 // to satisfy SdkLibraryDependency interface
 func (module *sdkLibraryImport) SdkImplementationJars(ctx android.BaseModuleContext, sdkVersion sdkSpec) android.Paths {
 	// This module is just a wrapper for the stubs.
-	return module.stubsPath
+	return module.sdkJars(ctx, sdkVersion)
 }
diff --git a/java/testing.go b/java/testing.go
index 08bae44..e746e2d 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -30,6 +30,7 @@
 		"b.kt":                   nil,
 		"a.jar":                  nil,
 		"b.jar":                  nil,
+		"c.jar":                  nil,
 		"APP_NOTICE":             nil,
 		"GENRULE_NOTICE":         nil,
 		"LIB_NOTICE":             nil,