Support mechanism to select a specific version of module sdk prebuilt

This CL is the java_(test_)_import equivalent of aosp/2928483.

With trunk stable, we will have
multiple prebuilts of the prebuilt java .jars in
prebuilts/module_sdk/art/<v>/test-exports/, and this CL introduces a
mechanism to use apex_contributions to select a specific versioned
prebuilt when building.

Implementation details: Create a new source_module_name property to
identify the root module. rdeps referring to the root module will get
redirected if necessary. This property also
becomes the stem, if `stem` is not set explicitly.

Bug: 322175508

Test: Added a unit test
Change-Id: I2dff38acdf2002355586dbb2be8c1f98af4741f7
diff --git a/java/androidmk.go b/java/androidmk.go
index c86dcf4..b523594 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -209,9 +209,10 @@
 		return []android.AndroidMkEntries{}
 	}
 	return []android.AndroidMkEntries{android.AndroidMkEntries{
-		Class:      "JAVA_LIBRARIES",
-		OutputFile: android.OptionalPathForPath(prebuilt.combinedClasspathFile),
-		Include:    "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
+		Class:        "JAVA_LIBRARIES",
+		OverrideName: prebuilt.BaseModuleName(),
+		OutputFile:   android.OptionalPathForPath(prebuilt.combinedClasspathFile),
+		Include:      "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
 			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 				entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", !Bool(prebuilt.properties.Installable))
diff --git a/java/java.go b/java/java.go
index d18d915..38690b1 100644
--- a/java/java.go
+++ b/java/java.go
@@ -2089,6 +2089,11 @@
 		// that depend on this module, as well as to aidl for this module.
 		Export_include_dirs []string
 	}
+
+	// Name of the source soong module that gets shadowed by this prebuilt
+	// If unspecified, follows the naming convention that the source module of
+	// the prebuilt is Name() without "prebuilt_" prefix
+	Source_module_name *string
 }
 
 type Import struct {
@@ -2162,12 +2167,16 @@
 	return j.properties.Jars
 }
 
+func (j *Import) BaseModuleName() string {
+	return proptools.StringDefault(j.properties.Source_module_name, j.ModuleBase.Name())
+}
+
 func (j *Import) Name() string {
 	return j.prebuilt.Name(j.ModuleBase.Name())
 }
 
 func (j *Import) Stem() string {
-	return proptools.StringDefault(j.properties.Stem, j.ModuleBase.Name())
+	return proptools.StringDefault(j.properties.Stem, j.BaseModuleName())
 }
 
 func (a *Import) JacocoReportClassesFile() android.Path {
diff --git a/java/java_test.go b/java/java_test.go
index 0891ab6..42301d8 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -24,6 +24,7 @@
 	"strings"
 	"testing"
 
+	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/aconfig"
@@ -2521,3 +2522,105 @@
 			apiScopePublic.stubsLibraryModuleName("foo"), "android_common",
 			apiScopePublic.apiLibraryModuleName("foo")))
 }
+
+func TestMultiplePrebuilts(t *testing.T) {
+	bp := `
+		// an rdep
+		java_library {
+			name: "foo",
+			libs: ["bar"],
+		}
+
+		// multiple variations of dep
+		// source
+		java_library {
+			name: "bar",
+			srcs: ["bar.java"],
+		}
+		// prebuilt "v1"
+		java_import {
+			name: "bar",
+			jars: ["bar.jar"],
+		}
+		// prebuilt "v2"
+		java_import {
+			name: "bar.v2",
+			source_module_name: "bar",
+			jars: ["bar.v1.jar"],
+		}
+
+		// selectors
+		apex_contributions {
+			name: "myapex_contributions",
+			contents: ["%v"],
+		}
+	`
+	hasDep := func(ctx *android.TestResult, m android.Module, wantDep android.Module) bool {
+		t.Helper()
+		var found bool
+		ctx.VisitDirectDeps(m, func(dep blueprint.Module) {
+			if dep == wantDep {
+				found = true
+			}
+		})
+		return found
+	}
+
+	hasFileWithStem := func(m android.TestingModule, stem string) bool {
+		t.Helper()
+		for _, o := range m.AllOutputs() {
+			_, file := filepath.Split(o)
+			if file == stem+".jar" {
+				return true
+			}
+		}
+		return false
+	}
+
+	testCases := []struct {
+		desc                   string
+		selectedDependencyName string
+		expectedDependencyName string
+	}{
+		{
+			desc:                   "Source library is selected using apex_contributions",
+			selectedDependencyName: "bar",
+			expectedDependencyName: "bar",
+		},
+		{
+			desc:                   "Prebuilt library v1 is selected using apex_contributions",
+			selectedDependencyName: "prebuilt_bar",
+			expectedDependencyName: "prebuilt_bar",
+		},
+		{
+			desc:                   "Prebuilt library v2 is selected using apex_contributions",
+			selectedDependencyName: "prebuilt_bar.v2",
+			expectedDependencyName: "prebuilt_bar.v2",
+		},
+	}
+
+	for _, tc := range testCases {
+		ctx := android.GroupFixturePreparers(
+			prepareForJavaTest,
+			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+				variables.BuildFlags = map[string]string{
+					"RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": "myapex_contributions",
+				}
+			}),
+		).RunTestWithBp(t, fmt.Sprintf(bp, tc.selectedDependencyName))
+
+		// check that rdep gets the correct variation of dep
+		foo := ctx.ModuleForTests("foo", "android_common")
+		expectedDependency := ctx.ModuleForTests(tc.expectedDependencyName, "android_common")
+		android.AssertBoolEquals(t, fmt.Sprintf("expected dependency from %s to %s\n", foo.Module().Name(), tc.expectedDependencyName), true, hasDep(ctx, foo.Module(), expectedDependency.Module()))
+
+		// check that output file of dep is always bar.jar
+		// The filename should be agnostic to source/prebuilt/prebuilt_version
+		android.AssertBoolEquals(t, fmt.Sprintf("could not find bar.jar in outputs of %s. All Outputs %v\n", tc.expectedDependencyName, expectedDependency.AllOutputs()), true, hasFileWithStem(expectedDependency, "bar"))
+
+		// check LOCAL_MODULE of the selected module name
+		// the prebuilt should have the same LOCAL_MODULE when exported to make
+		entries := android.AndroidMkEntriesForTest(t, ctx.TestContext, expectedDependency.Module())[0]
+		android.AssertStringEquals(t, "unexpected LOCAL_MODULE", "bar", entries.EntryMap["LOCAL_MODULE"][0])
+	}
+}