Merge changes from topic "retry-jsl-stuff" into rvc-dev

* changes:
  Retry: "java_sdk_library: Do not expose stubs implementation jar"
  Retry: "java_sdk_library: Access outputs using tags"
  Retry: "java_sdk_library: Improve consistency with ..._import"
  Retry: "java_sdk_library: Add redirection to module-lib stubs"
  Retry: "java_sdk_library: Extract common stubs redirect code"
diff --git a/java/droiddoc.go b/java/droiddoc.go
index 4b4a245..c1cecb6 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -350,11 +350,16 @@
 	ApiFilePath() android.Path
 }
 
+type ApiStubsSrcProvider interface {
+	StubsSrcJar() android.Path
+}
+
 // Provider of information about API stubs, used by java_sdk_library.
 type ApiStubsProvider interface {
 	ApiFilePath
 	RemovedApiFilePath() android.Path
-	StubsSrcJar() android.Path
+
+	ApiStubsSrcProvider
 }
 
 //
@@ -1912,6 +1917,10 @@
 	}
 }
 
+func (d *PrebuiltStubsSources) StubsSrcJar() android.Path {
+	return d.stubsSrcJar
+}
+
 func (p *PrebuiltStubsSources) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	p.stubsSrcJar = android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"stubs.srcjar")
 
diff --git a/java/java_test.go b/java/java_test.go
index 01ddccf..4f3a803 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -575,6 +575,7 @@
 			},
 			test: {
 				jars: ["c.jar"],
+				stub_srcs: ["c.java"],
 			},
 		}
 		`)
@@ -1229,6 +1230,113 @@
 	}
 }
 
+func TestJavaSdkLibrary_UseSourcesFromAnotherSdkLibrary(t *testing.T) {
+	testJava(t, `
+		java_sdk_library {
+			name: "foo",
+			srcs: ["a.java"],
+			api_packages: ["foo"],
+			public: {
+				enabled: true,
+			},
+		}
+
+		java_library {
+			name: "bar",
+			srcs: ["b.java", ":foo{.public.stubs.source}"],
+		}
+		`)
+}
+
+func TestJavaSdkLibrary_AccessOutputFiles_MissingScope(t *testing.T) {
+	testJavaError(t, `"foo" does not provide api scope system`, `
+		java_sdk_library {
+			name: "foo",
+			srcs: ["a.java"],
+			api_packages: ["foo"],
+			public: {
+				enabled: true,
+			},
+		}
+
+		java_library {
+			name: "bar",
+			srcs: ["b.java", ":foo{.system.stubs.source}"],
+		}
+		`)
+}
+
+func TestJavaSdkLibraryImport_AccessOutputFiles(t *testing.T) {
+	testJava(t, `
+		java_sdk_library_import {
+			name: "foo",
+			public: {
+				jars: ["a.jar"],
+				stub_srcs: ["a.java"],
+				current_api: "api/current.txt",
+				removed_api: "api/removed.txt",
+			},
+		}
+
+		java_library {
+			name: "bar",
+			srcs: [":foo{.public.stubs.source}"],
+			java_resources: [
+				":foo{.public.api.txt}",
+				":foo{.public.removed-api.txt}",
+			],
+		}
+		`)
+}
+
+func TestJavaSdkLibraryImport_AccessOutputFiles_Invalid(t *testing.T) {
+	bp := `
+		java_sdk_library_import {
+			name: "foo",
+			public: {
+				jars: ["a.jar"],
+			},
+		}
+		`
+
+	t.Run("stubs.source", func(t *testing.T) {
+		testJavaError(t, `stubs.source not available for api scope public`, bp+`
+		java_library {
+			name: "bar",
+			srcs: [":foo{.public.stubs.source}"],
+			java_resources: [
+				":foo{.public.api.txt}",
+				":foo{.public.removed-api.txt}",
+			],
+		}
+		`)
+	})
+
+	t.Run("api.txt", func(t *testing.T) {
+		testJavaError(t, `api.txt not available for api scope public`, bp+`
+		java_library {
+			name: "bar",
+			srcs: ["a.java"],
+			java_resources: [
+				":foo{.public.api.txt}",
+			],
+		}
+		`)
+	})
+
+	t.Run("removed-api.txt", func(t *testing.T) {
+		testJavaError(t, `removed-api.txt not available for api scope public`, bp+`
+		java_library {
+			name: "bar",
+			srcs: ["a.java"],
+			java_resources: [
+				":foo{.public.removed-api.txt}",
+			],
+		}
+		`)
+	})
+}
+
 func TestJavaSdkLibrary_InvalidScopes(t *testing.T) {
 	testJavaError(t, `module "foo": enabled api scope "system" depends on disabled scope "public"`, `
 		java_sdk_library {
@@ -1261,6 +1369,45 @@
 		`)
 }
 
+func TestJavaSdkLibrary_MissingScope(t *testing.T) {
+	testJavaError(t, `requires api scope module-lib from foo but it only has \[\] available`, `
+		java_sdk_library {
+			name: "foo",
+			srcs: ["a.java"],
+			public: {
+				enabled: false,
+			},
+		}
+
+		java_library {
+			name: "baz",
+			srcs: ["a.java"],
+			libs: ["foo"],
+			sdk_version: "module_current",
+		}
+		`)
+}
+
+func TestJavaSdkLibrary_FallbackScope(t *testing.T) {
+	testJava(t, `
+		java_sdk_library {
+			name: "foo",
+			srcs: ["a.java"],
+			system: {
+				enabled: true,
+			},
+		}
+
+		java_library {
+			name: "baz",
+			srcs: ["a.java"],
+			libs: ["foo"],
+			// foo does not have module-lib scope so it should fallback to system
+			sdk_version: "module_current",
+		}
+		`)
+}
+
 var compilerFlagsTestCases = []struct {
 	in  string
 	out bool
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 9d5c7b6..adbb5d2 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -19,6 +19,7 @@
 	"path"
 	"path/filepath"
 	"reflect"
+	"regexp"
 	"sort"
 	"strings"
 	"sync"
@@ -145,6 +146,8 @@
 // Initialize a scope, creating and adding appropriate dependency tags
 func initApiScope(scope *apiScope) *apiScope {
 	name := scope.name
+	scopeByName[name] = scope
+	allScopeNames = append(allScopeNames, name)
 	scope.propertyName = strings.ReplaceAll(name, "-", "_")
 	scope.fieldName = proptools.FieldNameForProperty(scope.propertyName)
 	scope.stubsTag = scopeDependencyTag{
@@ -217,6 +220,8 @@
 }
 
 var (
+	scopeByName    = make(map[string]*apiScope)
+	allScopeNames  []string
 	apiScopePublic = initApiScope(&apiScope{
 		name: "public",
 
@@ -433,12 +438,30 @@
 	//Html_doc *bool
 }
 
+// Paths to outputs from java_sdk_library and java_sdk_library_import.
+//
+// Fields that are android.Paths are always set (during GenerateAndroidBuildActions).
+// OptionalPaths are always set by java_sdk_library but may not be set by
+// java_sdk_library_import as not all instances provide that information.
 type scopePaths struct {
-	stubsHeaderPath    android.Paths
-	stubsImplPath      android.Paths
-	currentApiFilePath android.Path
-	removedApiFilePath android.Path
-	stubsSrcJar        android.Path
+	// The path (represented as Paths for convenience when returning) to the stubs header jar.
+	//
+	// That is the jar that is created by turbine.
+	stubsHeaderPath android.Paths
+
+	// The path (represented as Paths for convenience when returning) to the stubs implementation jar.
+	//
+	// This is not the implementation jar, it still only contains stubs.
+	stubsImplPath android.Paths
+
+	// The API specification file, e.g. system_current.txt.
+	currentApiFilePath android.OptionalPath
+
+	// The specification of API elements removed since the last release.
+	removedApiFilePath android.OptionalPath
+
+	// The stubs source jar.
+	stubsSrcJar android.OptionalPath
 }
 
 func (paths *scopePaths) extractStubsLibraryInfoFromDependency(dep android.Module) error {
@@ -460,9 +483,18 @@
 	}
 }
 
+func (paths *scopePaths) treatDepAsApiStubsSrcProvider(dep android.Module, action func(provider ApiStubsSrcProvider)) error {
+	if apiStubsProvider, ok := dep.(ApiStubsSrcProvider); ok {
+		action(apiStubsProvider)
+		return nil
+	} else {
+		return fmt.Errorf("expected module that implements ApiStubsSrcProvider, e.g. droidstubs")
+	}
+}
+
 func (paths *scopePaths) extractApiInfoFromApiStubsProvider(provider ApiStubsProvider) {
-	paths.currentApiFilePath = provider.ApiFilePath()
-	paths.removedApiFilePath = provider.RemovedApiFilePath()
+	paths.currentApiFilePath = android.OptionalPathForPath(provider.ApiFilePath())
+	paths.removedApiFilePath = android.OptionalPathForPath(provider.RemovedApiFilePath())
 }
 
 func (paths *scopePaths) extractApiInfoFromDep(dep android.Module) error {
@@ -471,12 +503,12 @@
 	})
 }
 
-func (paths *scopePaths) extractStubsSourceInfoFromApiStubsProviders(provider ApiStubsProvider) {
-	paths.stubsSrcJar = provider.StubsSrcJar()
+func (paths *scopePaths) extractStubsSourceInfoFromApiStubsProviders(provider ApiStubsSrcProvider) {
+	paths.stubsSrcJar = android.OptionalPathForPath(provider.StubsSrcJar())
 }
 
 func (paths *scopePaths) extractStubsSourceInfoFromDep(dep android.Module) error {
-	return paths.treatDepAsApiStubsProvider(dep, func(provider ApiStubsProvider) {
+	return paths.treatDepAsApiStubsSrcProvider(dep, func(provider ApiStubsSrcProvider) {
 		paths.extractStubsSourceInfoFromApiStubsProviders(provider)
 	})
 }
@@ -551,7 +583,83 @@
 	return c.namingScheme.apiModuleName(apiScope, c.moduleBase.BaseModuleName())
 }
 
-func (c *commonToSdkLibraryAndImport) getScopePaths(scope *apiScope) *scopePaths {
+// The component names for different outputs of the java_sdk_library.
+//
+// They are similar to the names used for the child modules it creates
+const (
+	stubsSourceComponentName = "stubs.source"
+
+	apiTxtComponentName = "api.txt"
+
+	removedApiTxtComponentName = "removed-api.txt"
+)
+
+// A regular expression to match tags that reference a specific stubs component.
+//
+// It will only match if given a valid scope and a valid component. It is verfy strict
+// to ensure it does not accidentally match a similar looking tag that should be processed
+// by the embedded Library.
+var tagSplitter = func() *regexp.Regexp {
+	// Given a list of literal string items returns a regular expression that will
+	// match any one of the items.
+	choice := func(items ...string) string {
+		return `\Q` + strings.Join(items, `\E|\Q`) + `\E`
+	}
+
+	// Regular expression to match one of the scopes.
+	scopesRegexp := choice(allScopeNames...)
+
+	// Regular expression to match one of the components.
+	componentsRegexp := choice(stubsSourceComponentName, apiTxtComponentName, removedApiTxtComponentName)
+
+	// Regular expression to match any combination of one scope and one component.
+	return regexp.MustCompile(fmt.Sprintf(`^\.(%s)\.(%s)$`, scopesRegexp, componentsRegexp))
+}()
+
+// For OutputFileProducer interface
+//
+// .<scope>.stubs.source
+// .<scope>.api.txt
+// .<scope>.removed-api.txt
+func (c *commonToSdkLibraryAndImport) commonOutputFiles(tag string) (android.Paths, error) {
+	if groups := tagSplitter.FindStringSubmatch(tag); groups != nil {
+		scopeName := groups[1]
+		component := groups[2]
+
+		if scope, ok := scopeByName[scopeName]; ok {
+			paths := c.findScopePaths(scope)
+			if paths == nil {
+				return nil, fmt.Errorf("%q does not provide api scope %s", c.moduleBase.BaseModuleName(), scopeName)
+			}
+
+			switch component {
+			case stubsSourceComponentName:
+				if paths.stubsSrcJar.Valid() {
+					return android.Paths{paths.stubsSrcJar.Path()}, nil
+				}
+
+			case apiTxtComponentName:
+				if paths.currentApiFilePath.Valid() {
+					return android.Paths{paths.currentApiFilePath.Path()}, nil
+				}
+
+			case removedApiTxtComponentName:
+				if paths.removedApiFilePath.Valid() {
+					return android.Paths{paths.removedApiFilePath.Path()}, nil
+				}
+			}
+
+			return nil, fmt.Errorf("%s not available for api scope %s", component, scopeName)
+		} else {
+			return nil, fmt.Errorf("unknown scope %s in %s", scope, tag)
+		}
+
+	} else {
+		return nil, nil
+	}
+}
+
+func (c *commonToSdkLibraryAndImport) getScopePathsCreateIfNeeded(scope *apiScope) *scopePaths {
 	if c.scopePaths == nil {
 		c.scopePaths = make(map[*apiScope]*scopePaths)
 	}
@@ -564,6 +672,62 @@
 	return paths
 }
 
+func (c *commonToSdkLibraryAndImport) findScopePaths(scope *apiScope) *scopePaths {
+	if c.scopePaths == nil {
+		return nil
+	}
+
+	return c.scopePaths[scope]
+}
+
+// If this does not support the requested api scope then find the closest available
+// scope it does support. Returns nil if no such scope is available.
+func (c *commonToSdkLibraryAndImport) findClosestScopePath(scope *apiScope) *scopePaths {
+	for s := scope; s != nil; s = s.extends {
+		if paths := c.findScopePaths(s); paths != nil {
+			return paths
+		}
+	}
+
+	// This should never happen outside tests as public should be the base scope for every
+	// scope and is enabled by default.
+	return nil
+}
+
+func (c *commonToSdkLibraryAndImport) selectHeaderJarsForSdkVersion(ctx android.BaseModuleContext, sdkVersion sdkSpec) android.Paths {
+
+	// If a specific numeric version has been requested then use prebuilt versions of the sdk.
+	if sdkVersion.version.isNumbered() {
+		return PrebuiltJars(ctx, c.moduleBase.BaseModuleName(), sdkVersion)
+	}
+
+	var apiScope *apiScope
+	switch sdkVersion.kind {
+	case sdkSystem:
+		apiScope = apiScopeSystem
+	case sdkModule:
+		apiScope = apiScopeModuleLib
+	case sdkTest:
+		apiScope = apiScopeTest
+	default:
+		apiScope = apiScopePublic
+	}
+
+	paths := c.findClosestScopePath(apiScope)
+	if paths == nil {
+		var scopes []string
+		for _, s := range allApiScopes {
+			if c.findScopePaths(s) != nil {
+				scopes = append(scopes, s.name)
+			}
+		}
+		ctx.ModuleErrorf("requires api scope %s from %s but it only has %q available", apiScope.name, c.moduleBase.BaseModuleName(), scopes)
+		return nil
+	}
+
+	return paths.stubsHeaderPath
+}
+
 type SdkLibrary struct {
 	Library
 
@@ -664,6 +828,15 @@
 	module.Library.deps(ctx)
 }
 
+func (module *SdkLibrary) OutputFiles(tag string) (android.Paths, error) {
+	paths, err := module.commonOutputFiles(tag)
+	if paths == nil && err == nil {
+		return module.Library.OutputFiles(tag)
+	} else {
+		return paths, err
+	}
+}
+
 func (module *SdkLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	// Don't build an implementation library if this is api only.
 	if !proptools.Bool(module.sdkLibraryProperties.Api_only) {
@@ -679,7 +852,7 @@
 		// Extract information from any of the scope specific dependencies.
 		if scopeTag, ok := tag.(scopeDependencyTag); ok {
 			apiScope := scopeTag.apiScope
-			scopePaths := module.getScopePaths(apiScope)
+			scopePaths := module.getScopePathsCreateIfNeeded(apiScope)
 
 			// Extract information from the dependency. The exact information extracted
 			// is determined by the nature of the dependency which is determined by the tag.
@@ -1012,41 +1185,20 @@
 	return android.Paths{jarPath.Path()}
 }
 
-func (module *SdkLibrary) sdkJars(
-	ctx android.BaseModuleContext,
-	sdkVersion sdkSpec,
-	headerJars bool) android.Paths {
+func (module *SdkLibrary) sdkJars(ctx android.BaseModuleContext, sdkVersion sdkSpec, headerJars bool) android.Paths {
 
-	// If a specific numeric version has been requested then use prebuilt versions of the sdk.
-	if sdkVersion.version.isNumbered() {
-		return PrebuiltJars(ctx, module.BaseModuleName(), sdkVersion)
-	} else {
-		if !sdkVersion.specified() {
-			if headerJars {
-				return module.HeaderJars()
-			} else {
-				return module.ImplementationJars()
-			}
-		}
-		var apiScope *apiScope
-		switch sdkVersion.kind {
-		case sdkSystem:
-			apiScope = apiScopeSystem
-		case sdkTest:
-			apiScope = apiScopeTest
-		case sdkPrivate:
-			return module.HeaderJars()
-		default:
-			apiScope = apiScopePublic
-		}
-
-		paths := module.getScopePaths(apiScope)
+	// Check any special cases for java_sdk_library.
+	if !sdkVersion.specified() {
 		if headerJars {
-			return paths.stubsHeaderPath
+			return module.HeaderJars()
 		} else {
-			return paths.stubsImplPath
+			return module.ImplementationJars()
 		}
+	} else if sdkVersion.kind == sdkPrivate {
+		return module.HeaderJars()
 	}
+
+	return module.selectHeaderJarsForSdkVersion(ctx, sdkVersion)
 }
 
 // to satisfy SdkLibraryDependency interface
@@ -1277,10 +1429,10 @@
 	Stub_srcs []string `android:"path"`
 
 	// The current.txt
-	Current_api string `android:"path"`
+	Current_api *string `android:"path"`
 
 	// The removed.txt
-	Removed_api string `android:"path"`
+	Removed_api *string `android:"path"`
 }
 
 type sdkLibraryImportProperties struct {
@@ -1390,7 +1542,9 @@
 
 		module.createJavaImportForStubs(mctx, apiScope, scopeProperties)
 
-		module.createPrebuiltStubsSources(mctx, apiScope, scopeProperties)
+		if len(scopeProperties.Stub_srcs) > 0 {
+			module.createPrebuiltStubsSources(mctx, apiScope, scopeProperties)
+		}
 	}
 
 	javaSdkLibraries := javaSdkLibraries(mctx.Config())
@@ -1442,45 +1596,48 @@
 
 		// Add dependencies to the prebuilt stubs library
 		ctx.AddVariationDependencies(nil, apiScope.stubsTag, module.stubsLibraryModuleName(apiScope))
+
+		if len(scopeProperties.Stub_srcs) > 0 {
+			// Add dependencies to the prebuilt stubs source library
+			ctx.AddVariationDependencies(nil, apiScope.stubsSourceTag, module.stubsSourceModuleName(apiScope))
+		}
 	}
 }
 
+func (module *sdkLibraryImport) OutputFiles(tag string) (android.Paths, error) {
+	return module.commonOutputFiles(tag)
+}
+
 func (module *sdkLibraryImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	// Record the paths to the prebuilt stubs library.
+	// Record the paths to the prebuilt stubs library and stubs source.
 	ctx.VisitDirectDeps(func(to android.Module) {
 		tag := ctx.OtherModuleDependencyTag(to)
 
-		if lib, ok := to.(Dependency); ok {
-			if scopeTag, ok := tag.(scopeDependencyTag); ok {
-				apiScope := scopeTag.apiScope
-				scopePaths := module.getScopePaths(apiScope)
-				scopePaths.stubsHeaderPath = lib.HeaderJars()
-			}
+		// Extract information from any of the scope specific dependencies.
+		if scopeTag, ok := tag.(scopeDependencyTag); ok {
+			apiScope := scopeTag.apiScope
+			scopePaths := module.getScopePathsCreateIfNeeded(apiScope)
+
+			// Extract information from the dependency. The exact information extracted
+			// is determined by the nature of the dependency which is determined by the tag.
+			scopeTag.extractDepInfo(ctx, to, scopePaths)
 		}
 	})
+
+	// Populate the scope paths with information from the properties.
+	for apiScope, scopeProperties := range module.scopeProperties {
+		if len(scopeProperties.Jars) == 0 {
+			continue
+		}
+
+		paths := module.getScopePathsCreateIfNeeded(apiScope)
+		paths.currentApiFilePath = android.OptionalPathForModuleSrc(ctx, scopeProperties.Current_api)
+		paths.removedApiFilePath = android.OptionalPathForModuleSrc(ctx, scopeProperties.Removed_api)
+	}
 }
 
-func (module *sdkLibraryImport) sdkJars(
-	ctx android.BaseModuleContext,
-	sdkVersion sdkSpec) android.Paths {
-
-	// If a specific numeric version has been requested then use prebuilt versions of the sdk.
-	if sdkVersion.version.isNumbered() {
-		return PrebuiltJars(ctx, module.BaseModuleName(), sdkVersion)
-	}
-
-	var apiScope *apiScope
-	switch sdkVersion.kind {
-	case sdkSystem:
-		apiScope = apiScopeSystem
-	case sdkTest:
-		apiScope = apiScopeTest
-	default:
-		apiScope = apiScopePublic
-	}
-
-	paths := module.getScopePaths(apiScope)
-	return paths.stubsHeaderPath
+func (module *sdkLibraryImport) sdkJars(ctx android.BaseModuleContext, sdkVersion sdkSpec) android.Paths {
+	return module.selectHeaderJarsForSdkVersion(ctx, sdkVersion)
 }
 
 // to satisfy SdkLibraryDependency interface
@@ -1653,15 +1810,19 @@
 
 	s.Scopes = make(map[*apiScope]scopeProperties)
 	for _, apiScope := range allApiScopes {
-		paths := sdk.getScopePaths(apiScope)
+		paths := sdk.findScopePaths(apiScope)
+		if paths == nil {
+			continue
+		}
+
 		jars := paths.stubsImplPath
 		if len(jars) > 0 {
 			properties := scopeProperties{}
 			properties.Jars = jars
 			properties.SdkVersion = sdk.sdkVersionForStubsLibrary(ctx.SdkModuleContext(), apiScope)
-			properties.StubsSrcJar = paths.stubsSrcJar
-			properties.CurrentApiFile = paths.currentApiFilePath
-			properties.RemovedApiFile = paths.removedApiFilePath
+			properties.StubsSrcJar = paths.stubsSrcJar.Path()
+			properties.CurrentApiFile = paths.currentApiFilePath.Path()
+			properties.RemovedApiFile = paths.removedApiFilePath.Path()
 			s.Scopes[apiScope] = properties
 		}
 	}