Introduce system_modules property to java_api_library

System_modules property provides the jars passed as bootclasspath when
compiling the stubs in the java_api_library where its creating
java_sdk_library's sdk_version is none, as the jars will not be provided
from the full_surface_stub_libs but compiled by itself in the child
change.

The jar provided by the system_modules will also be passed to metalava
to resolve hierarchy coming from outer dependencies.

Test: m --build-from-text-stub
Bug: 288624417
Change-Id: I8f3b89efa24bceb070d7a37fae3c7334dd7f0868
diff --git a/java/java.go b/java/java.go
index cac49a2..bde35e8 100644
--- a/java/java.go
+++ b/java/java.go
@@ -1689,6 +1689,12 @@
 
 	// Version of previously released API file for compatibility check.
 	Previous_api *string `android:"path"`
+
+	// java_system_modules module providing the jar to be added to the
+	// bootclasspath when compiling the stubs.
+	// The jar will also be passed to metalava as a classpath to
+	// generate compilable stubs.
+	System_modules *string
 }
 
 func ApiLibraryFactory() android.Module {
@@ -1708,7 +1714,8 @@
 }
 
 func metalavaStubCmd(ctx android.ModuleContext, rule *android.RuleBuilder,
-	srcs android.Paths, homeDir android.WritablePath) *android.RuleBuilderCommand {
+	srcs android.Paths, homeDir android.WritablePath,
+	classpath android.Paths) *android.RuleBuilderCommand {
 	rule.Command().Text("rm -rf").Flag(homeDir.String())
 	rule.Command().Text("mkdir -p").Flag(homeDir.String())
 
@@ -1747,12 +1754,17 @@
 		FlagWithArg("--hide ", "InvalidNullabilityOverride").
 		FlagWithArg("--hide ", "ChangedDefault")
 
-	// The main purpose of the `--api-class-resolution api` option is to force metalava to ignore
-	// classes on the classpath when an API file contains missing classes. However, as this command
-	// does not specify `--classpath` this is not needed for that. However, this is also used as a
-	// signal to the special metalava code for generating stubs from text files that it needs to add
-	// some additional items into the API (e.g. default constructors).
-	cmd.FlagWithArg("--api-class-resolution ", "api")
+	if len(classpath) == 0 {
+		// The main purpose of the `--api-class-resolution api` option is to force metalava to ignore
+		// classes on the classpath when an API file contains missing classes. However, as this command
+		// does not specify `--classpath` this is not needed for that. However, this is also used as a
+		// signal to the special metalava code for generating stubs from text files that it needs to add
+		// some additional items into the API (e.g. default constructors).
+		cmd.FlagWithArg("--api-class-resolution ", "api")
+	} else {
+		cmd.FlagWithArg("--api-class-resolution ", "api:classpath")
+		cmd.FlagWithInputList("--classpath ", classpath, ":")
+	}
 
 	return cmd
 }
@@ -1815,6 +1827,9 @@
 	if al.properties.Full_api_surface_stub != nil {
 		ctx.AddVariationDependencies(nil, depApiSrcsTag, String(al.properties.Full_api_surface_stub))
 	}
+	if al.properties.System_modules != nil {
+		ctx.AddVariationDependencies(nil, systemModulesTag, String(al.properties.System_modules))
+	}
 }
 
 // Map where key is the api scope name and value is the int value
@@ -1854,6 +1869,7 @@
 	var classPaths android.Paths
 	var staticLibs android.Paths
 	var depApiSrcsStubsJar android.Path
+	var systemModulesPaths android.Paths
 	ctx.VisitDirectDeps(func(dep android.Module) {
 		tag := ctx.OtherModuleDependencyTag(dep)
 		switch tag {
@@ -1872,6 +1888,9 @@
 		case depApiSrcsTag:
 			provider := ctx.OtherModuleProvider(dep, JavaInfoProvider).(JavaInfo)
 			depApiSrcsStubsJar = provider.HeaderJars[0]
+		case systemModulesTag:
+			module := dep.(SystemModulesProvider)
+			systemModulesPaths = append(systemModulesPaths, module.HeaderJars()...)
 		}
 	})
 
@@ -1885,7 +1904,7 @@
 		ctx.ModuleErrorf("Error: %s has an empty api file.", ctx.ModuleName())
 	}
 
-	cmd := metalavaStubCmd(ctx, rule, srcFiles, homeDir)
+	cmd := metalavaStubCmd(ctx, rule, srcFiles, homeDir, systemModulesPaths)
 
 	al.stubsFlags(ctx, cmd, stubsDir)
 
@@ -1917,6 +1936,7 @@
 		flags.javaVersion = getStubsJavaVersion()
 		flags.javacFlags = strings.Join(al.properties.Javacflags, " ")
 		flags.classpath = classpath(classPaths)
+		flags.bootClasspath = classpath(systemModulesPaths)
 
 		annoSrcJar := android.PathForModuleOut(ctx, ctx.ModuleName(), "anno.srcjar")
 
diff --git a/java/java_test.go b/java/java_test.go
index d51604a..6b8b735 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -2447,3 +2447,39 @@
 		"default/java/api/module-lib-current.txt default/java/api/system-server-current.txt"
 	android.AssertStringDoesContain(t, "source text files not in api scope order", manifestCommand, sourceFilesFlag)
 }
+
+func TestSdkLibraryProvidesSystemModulesToApiLibrary(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		prepareForJavaTest,
+		PrepareForTestWithJavaSdkLibraryFiles,
+		FixtureWithLastReleaseApis("foo"),
+		android.FixtureModifyConfig(func(config android.Config) {
+			config.SetApiLibraries([]string{"foo"})
+		}),
+		android.FixtureMergeMockFs(
+			map[string][]byte{
+				"A.java": nil,
+			},
+		),
+	).RunTestWithBp(t, `
+		java_library {
+			name: "bar",
+			srcs: ["a.java"],
+		}
+		java_system_modules {
+			name: "baz",
+			libs: ["bar"],
+		}
+		java_sdk_library {
+			name: "foo",
+			srcs: ["A.java"],
+			system_modules: "baz",
+		}
+	`)
+	m := result.ModuleForTests(apiScopePublic.apiLibraryModuleName("foo"), "android_common")
+	manifest := m.Output("metalava.sbox.textproto")
+	sboxProto := android.RuleBuilderSboxProtoForTests(t, manifest)
+	manifestCommand := sboxProto.Commands[0].GetCommand()
+	classPathFlag := "--classpath __SBOX_SANDBOX_DIR__/out/.intermediates/bar/android_common/turbine-combined/bar.jar"
+	android.AssertStringDoesContain(t, "command expected to contain classpath flag", manifestCommand, classPathFlag)
+}
diff --git a/java/sdk_library.go b/java/sdk_library.go
index e410a41..f72c608 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -1837,6 +1837,7 @@
 		Libs                  []string
 		Static_libs           []string
 		Full_api_surface_stub *string
+		System_modules        *string
 	}{}
 
 	props.Name = proptools.StringPtr(module.apiLibraryModuleName(apiScope))
@@ -1875,6 +1876,8 @@
 		props.Full_api_surface_stub = proptools.StringPtr(apiScope.kind.DefaultJavaLibraryName() + "_full.from-text")
 	}
 
+	props.System_modules = module.deviceProperties.System_modules
+
 	mctx.CreateModule(ApiLibraryFactory, &props)
 }