Move java.sdkSpec to the android package

... in preparation for making the handling of sdk versions consistent
across java and cc modules.

Bug: 175678607
Test: m
Change-Id: I598f0454bce9b7320621022115412fbe97403945
diff --git a/java/sdk.go b/java/sdk.go
index 74d5a81..b546ca0 100644
--- a/java/sdk.go
+++ b/java/sdk.go
@@ -19,7 +19,6 @@
 	"path/filepath"
 	"sort"
 	"strconv"
-	"strings"
 
 	"android/soong/android"
 	"android/soong/java/config"
@@ -38,19 +37,6 @@
 var nonUpdatableFrameworkAidlPathKey = android.NewOnceKey("nonUpdatableFrameworkAidlPathKey")
 var apiFingerprintPathKey = android.NewOnceKey("apiFingerprintPathKey")
 
-type sdkContext interface {
-	// sdkVersion returns sdkSpec that corresponds to the sdk_version property of the current module
-	sdkVersion() sdkSpec
-	// systemModules returns the system_modules property of the current module, or an empty string if it is not set.
-	systemModules() string
-	// minSdkVersion returns sdkSpec that corresponds to the min_sdk_version property of the current module,
-	// or from sdk_version if it is not set.
-	minSdkVersion() sdkSpec
-	// targetSdkVersion returns the sdkSpec that corresponds to the target_sdk_version property of the current module,
-	// or from sdk_version if it is not set.
-	targetSdkVersion() sdkSpec
-}
-
 func UseApiFingerprint(ctx android.BaseModuleContext) bool {
 	if ctx.Config().UnbundledBuild() &&
 		!ctx.Config().AlwaysUsePrebuiltSdks() &&
@@ -60,209 +46,8 @@
 	return false
 }
 
-// sdkKind represents a particular category of an SDK spec like public, system, test, etc.
-type sdkKind int
-
-const (
-	sdkInvalid sdkKind = iota
-	sdkNone
-	sdkCore
-	sdkCorePlatform
-	sdkPublic
-	sdkSystem
-	sdkTest
-	sdkModule
-	sdkSystemServer
-	sdkPrivate
-)
-
-// String returns the string representation of this sdkKind
-func (k sdkKind) String() string {
-	switch k {
-	case sdkPrivate:
-		return "private"
-	case sdkNone:
-		return "none"
-	case sdkPublic:
-		return "public"
-	case sdkSystem:
-		return "system"
-	case sdkTest:
-		return "test"
-	case sdkCore:
-		return "core"
-	case sdkCorePlatform:
-		return "core_platform"
-	case sdkModule:
-		return "module-lib"
-	case sdkSystemServer:
-		return "system-server"
-	default:
-		return "invalid"
-	}
-}
-
-// sdkVersion represents a specific version number of an SDK spec of a particular kind
-type sdkVersion int
-
-const (
-	// special version number for a not-yet-frozen SDK
-	sdkVersionCurrent sdkVersion = sdkVersion(android.FutureApiLevelInt)
-	// special version number to be used for SDK specs where version number doesn't
-	// make sense, e.g. "none", "", etc.
-	sdkVersionNone sdkVersion = sdkVersion(0)
-)
-
-// isCurrent checks if the sdkVersion refers to the not-yet-published version of an sdkKind
-func (v sdkVersion) isCurrent() bool {
-	return v == sdkVersionCurrent
-}
-
-// isNumbered checks if the sdkVersion refers to the published (a.k.a numbered) version of an sdkKind
-func (v sdkVersion) isNumbered() bool {
-	return !v.isCurrent() && v != sdkVersionNone
-}
-
-// String returns the string representation of this sdkVersion.
-func (v sdkVersion) String() string {
-	if v.isCurrent() {
-		return "current"
-	} else if v.isNumbered() {
-		return strconv.Itoa(int(v))
-	}
-	return "(no version)"
-}
-
-func (v sdkVersion) ApiLevel(ctx android.EarlyModuleContext) android.ApiLevel {
-	return android.ApiLevelOrPanic(ctx, v.String())
-}
-
-// asNumberString directly converts the numeric value of this sdk version as a string.
-// When isNumbered() is true, this method is the same as String(). However, for sdkVersionCurrent
-// and sdkVersionNone, this returns 10000 and 0 while String() returns "current" and "(no version"),
-// respectively.
-func (v sdkVersion) asNumberString() string {
-	return strconv.Itoa(int(v))
-}
-
-// sdkSpec represents the kind and the version of an SDK for a module to build against
-type sdkSpec struct {
-	kind    sdkKind
-	version sdkVersion
-	raw     string
-}
-
-func (s sdkSpec) String() string {
-	return fmt.Sprintf("%s_%s", s.kind, s.version)
-}
-
-// valid checks if this sdkSpec is well-formed. Note however that true doesn't mean that the
-// specified SDK actually exists.
-func (s sdkSpec) valid() bool {
-	return s.kind != sdkInvalid
-}
-
-// specified checks if this sdkSpec is well-formed and is not "".
-func (s sdkSpec) specified() bool {
-	return s.valid() && s.kind != sdkPrivate
-}
-
-// whether the API surface is managed and versioned, i.e. has .txt file that
-// get frozen on SDK freeze and changes get reviewed by API council.
-func (s sdkSpec) stable() bool {
-	if !s.specified() {
-		return false
-	}
-	switch s.kind {
-	case sdkNone:
-		// there is nothing to manage and version in this case; de facto stable API.
-		return true
-	case sdkCore, sdkPublic, sdkSystem, sdkModule, sdkSystemServer:
-		return true
-	case sdkCorePlatform, sdkTest, sdkPrivate:
-		return false
-	default:
-		panic(fmt.Errorf("unknown sdkKind=%v", s.kind))
-	}
-	return false
-}
-
-// prebuiltSdkAvailableForUnbundledBuilt tells whether this sdkSpec can have a prebuilt SDK
-// that can be used for unbundled builds.
-func (s sdkSpec) prebuiltSdkAvailableForUnbundledBuild() bool {
-	// "", "none", and "core_platform" are not available for unbundled build
-	// as we don't/can't have prebuilt stub for the versions
-	return s.kind != sdkPrivate && s.kind != sdkNone && s.kind != sdkCorePlatform
-}
-
-func (s sdkSpec) forVendorPartition(ctx android.EarlyModuleContext) sdkSpec {
-	// If BOARD_CURRENT_API_LEVEL_FOR_VENDOR_MODULES has a numeric value,
-	// use it instead of "current" for the vendor partition.
-	currentSdkVersion := ctx.DeviceConfig().CurrentApiLevelForVendorModules()
-	if currentSdkVersion == "current" {
-		return s
-	}
-
-	if s.kind == sdkPublic || s.kind == sdkSystem {
-		if s.version.isCurrent() {
-			if i, err := strconv.Atoi(currentSdkVersion); err == nil {
-				version := sdkVersion(i)
-				return sdkSpec{s.kind, version, s.raw}
-			}
-			panic(fmt.Errorf("BOARD_CURRENT_API_LEVEL_FOR_VENDOR_MODULES must be either \"current\" or a number, but was %q", currentSdkVersion))
-		}
-	}
-	return s
-}
-
-// usePrebuilt determines whether prebuilt SDK should be used for this sdkSpec with the given context.
-func (s sdkSpec) usePrebuilt(ctx android.EarlyModuleContext) bool {
-	if s.version.isCurrent() {
-		// "current" can be built from source and be from prebuilt SDK
-		return ctx.Config().AlwaysUsePrebuiltSdks()
-	} else if s.version.isNumbered() {
-		// validation check
-		if s.kind != sdkPublic && s.kind != sdkSystem && s.kind != sdkTest && s.kind != sdkModule {
-			panic(fmt.Errorf("prebuilt SDK is not not available for sdkKind=%q", s.kind))
-			return false
-		}
-		// numbered SDKs are always from prebuilt
-		return true
-	}
-	// "", "none", "core_platform" fall here
-	return false
-}
-
-// effectiveVersion converts an sdkSpec into the concrete sdkVersion that the module
-// should use. For modules targeting an unreleased SDK (meaning it does not yet have a number)
-// it returns android.FutureApiLevel(10000).
-func (s sdkSpec) effectiveVersion(ctx android.EarlyModuleContext) (sdkVersion, error) {
-	if !s.valid() {
-		return s.version, fmt.Errorf("invalid sdk version %q", s.raw)
-	}
-
-	if ctx.DeviceSpecific() || ctx.SocSpecific() {
-		s = s.forVendorPartition(ctx)
-	}
-	if s.version.isNumbered() {
-		return s.version, nil
-	}
-	return sdkVersion(ctx.Config().DefaultAppTargetSdk(ctx).FinalOrFutureInt()), nil
-}
-
-// effectiveVersionString converts an sdkSpec into the concrete version string that the module
-// should use. For modules targeting an unreleased SDK (meaning it does not yet have a number)
-// it returns the codename (P, Q, R, etc.)
-func (s sdkSpec) effectiveVersionString(ctx android.EarlyModuleContext) (string, error) {
-	ver, err := s.effectiveVersion(ctx)
-	if err == nil && int(ver) == ctx.Config().DefaultAppTargetSdk(ctx).FinalOrFutureInt() {
-		return ctx.Config().DefaultAppTargetSdk(ctx).String(), nil
-	}
-	return ver.String(), err
-}
-
-func (s sdkSpec) defaultJavaLanguageVersion(ctx android.EarlyModuleContext) javaVersion {
-	sdk, err := s.effectiveVersion(ctx)
+func defaultJavaLanguageVersion(ctx android.EarlyModuleContext, s android.SdkSpec) javaVersion {
+	sdk, err := s.EffectiveVersion(ctx)
 	if err != nil {
 		ctx.PropertyErrorf("sdk_version", "%s", err)
 	}
@@ -275,103 +60,27 @@
 	}
 }
 
-func sdkSpecFrom(str string) sdkSpec {
-	switch str {
-	// special cases first
-	case "":
-		return sdkSpec{sdkPrivate, sdkVersionNone, str}
-	case "none":
-		return sdkSpec{sdkNone, sdkVersionNone, str}
-	case "core_platform":
-		return sdkSpec{sdkCorePlatform, sdkVersionNone, str}
-	default:
-		// the syntax is [kind_]version
-		sep := strings.LastIndex(str, "_")
-
-		var kindString string
-		if sep == 0 {
-			return sdkSpec{sdkInvalid, sdkVersionNone, str}
-		} else if sep == -1 {
-			kindString = ""
-		} else {
-			kindString = str[0:sep]
-		}
-		versionString := str[sep+1 : len(str)]
-
-		var kind sdkKind
-		switch kindString {
-		case "":
-			kind = sdkPublic
-		case "core":
-			kind = sdkCore
-		case "system":
-			kind = sdkSystem
-		case "test":
-			kind = sdkTest
-		case "module":
-			kind = sdkModule
-		case "system_server":
-			kind = sdkSystemServer
-		default:
-			return sdkSpec{sdkInvalid, sdkVersionNone, str}
-		}
-
-		var version sdkVersion
-		if versionString == "current" {
-			version = sdkVersionCurrent
-		} else if i, err := strconv.Atoi(versionString); err == nil {
-			version = sdkVersion(i)
-		} else {
-			return sdkSpec{sdkInvalid, sdkVersionNone, str}
-		}
-
-		return sdkSpec{kind, version, str}
-	}
-}
-
-func (s sdkSpec) validateSystemSdk(ctx android.EarlyModuleContext) bool {
-	// Ensures that the specified system SDK version is one of BOARD_SYSTEMSDK_VERSIONS (for vendor/product Java module)
-	// Assuming that BOARD_SYSTEMSDK_VERSIONS := 28 29,
-	// sdk_version of the modules in vendor/product that use system sdk must be either system_28, system_29 or system_current
-	if s.kind != sdkSystem || !s.version.isNumbered() {
-		return true
-	}
-	allowedVersions := ctx.DeviceConfig().PlatformSystemSdkVersions()
-	if ctx.DeviceSpecific() || ctx.SocSpecific() || (ctx.ProductSpecific() && ctx.Config().EnforceProductPartitionInterface()) {
-		systemSdkVersions := ctx.DeviceConfig().SystemSdkVersions()
-		if len(systemSdkVersions) > 0 {
-			allowedVersions = systemSdkVersions
-		}
-	}
-	if len(allowedVersions) > 0 && !android.InList(s.version.String(), allowedVersions) {
-		ctx.PropertyErrorf("sdk_version", "incompatible sdk version %q. System SDK version should be one of %q",
-			s.raw, allowedVersions)
-		return false
-	}
-	return true
-}
-
-func decodeSdkDep(ctx android.EarlyModuleContext, sdkContext sdkContext) sdkDep {
-	sdkVersion := sdkContext.sdkVersion()
-	if !sdkVersion.valid() {
-		ctx.PropertyErrorf("sdk_version", "invalid version %q", sdkVersion.raw)
+func decodeSdkDep(ctx android.EarlyModuleContext, sdkContext android.SdkContext) sdkDep {
+	sdkVersion := sdkContext.SdkVersion()
+	if !sdkVersion.Valid() {
+		ctx.PropertyErrorf("sdk_version", "invalid version %q", sdkVersion.Raw)
 		return sdkDep{}
 	}
 
 	if ctx.DeviceSpecific() || ctx.SocSpecific() {
-		sdkVersion = sdkVersion.forVendorPartition(ctx)
+		sdkVersion = sdkVersion.ForVendorPartition(ctx)
 	}
 
-	if !sdkVersion.validateSystemSdk(ctx) {
+	if !sdkVersion.ValidateSystemSdk(ctx) {
 		return sdkDep{}
 	}
 
-	if sdkVersion.usePrebuilt(ctx) {
-		dir := filepath.Join("prebuilts", "sdk", sdkVersion.version.String(), sdkVersion.kind.String())
+	if sdkVersion.UsePrebuilt(ctx) {
+		dir := filepath.Join("prebuilts", "sdk", sdkVersion.Version.String(), sdkVersion.Kind.String())
 		jar := filepath.Join(dir, "android.jar")
 		// There's no aidl for other SDKs yet.
 		// TODO(77525052): Add aidl files for other SDKs too.
-		publicDir := filepath.Join("prebuilts", "sdk", sdkVersion.version.String(), "public")
+		publicDir := filepath.Join("prebuilts", "sdk", sdkVersion.Version.String(), "public")
 		aidl := filepath.Join(publicDir, "framework.aidl")
 		jarPath := android.ExistentPathForSource(ctx, jar)
 		aidlPath := android.ExistentPathForSource(ctx, aidl)
@@ -380,23 +89,23 @@
 		if (!jarPath.Valid() || !aidlPath.Valid()) && ctx.Config().AllowMissingDependencies() {
 			return sdkDep{
 				invalidVersion: true,
-				bootclasspath:  []string{fmt.Sprintf("sdk_%s_%s_android", sdkVersion.kind, sdkVersion.version.String())},
+				bootclasspath:  []string{fmt.Sprintf("sdk_%s_%s_android", sdkVersion.Kind, sdkVersion.Version.String())},
 			}
 		}
 
 		if !jarPath.Valid() {
-			ctx.PropertyErrorf("sdk_version", "invalid sdk version %q, %q does not exist", sdkVersion.raw, jar)
+			ctx.PropertyErrorf("sdk_version", "invalid sdk version %q, %q does not exist", sdkVersion.Raw, jar)
 			return sdkDep{}
 		}
 
 		if !aidlPath.Valid() {
-			ctx.PropertyErrorf("sdk_version", "invalid sdk version %q, %q does not exist", sdkVersion.raw, aidl)
+			ctx.PropertyErrorf("sdk_version", "invalid sdk version %q, %q does not exist", sdkVersion.Raw, aidl)
 			return sdkDep{}
 		}
 
 		var systemModules string
-		if sdkVersion.defaultJavaLanguageVersion(ctx).usesJavaModules() {
-			systemModules = "sdk_public_" + sdkVersion.version.String() + "_system_modules"
+		if defaultJavaLanguageVersion(ctx, sdkVersion).usesJavaModules() {
+			systemModules = "sdk_public_" + sdkVersion.Version.String() + "_system_modules"
 		}
 
 		return sdkDep{
@@ -418,8 +127,8 @@
 		}
 	}
 
-	switch sdkVersion.kind {
-	case sdkPrivate:
+	switch sdkVersion.Kind {
+	case android.SdkPrivate:
 		return sdkDep{
 			useModule:          true,
 			systemModules:      corePlatformSystemModules(ctx),
@@ -427,8 +136,8 @@
 			classpath:          config.FrameworkLibraries,
 			frameworkResModule: "framework-res",
 		}
-	case sdkNone:
-		systemModules := sdkContext.systemModules()
+	case android.SdkNone:
+		systemModules := sdkContext.SystemModules()
 		if systemModules == "" {
 			ctx.PropertyErrorf("sdk_version",
 				`system_modules is required to be set to a non-empty value when sdk_version is "none", did you mean sdk_version: "core_platform"?`)
@@ -444,34 +153,34 @@
 			systemModules:  systemModules,
 			bootclasspath:  []string{systemModules},
 		}
-	case sdkCorePlatform:
+	case android.SdkCorePlatform:
 		return sdkDep{
 			useModule:        true,
 			systemModules:    corePlatformSystemModules(ctx),
 			bootclasspath:    corePlatformBootclasspathLibraries(ctx),
 			noFrameworksLibs: true,
 		}
-	case sdkPublic:
+	case android.SdkPublic:
 		return toModule([]string{"android_stubs_current"}, "framework-res", sdkFrameworkAidlPath(ctx))
-	case sdkSystem:
+	case android.SdkSystem:
 		return toModule([]string{"android_system_stubs_current"}, "framework-res", sdkFrameworkAidlPath(ctx))
-	case sdkTest:
+	case android.SdkTest:
 		return toModule([]string{"android_test_stubs_current"}, "framework-res", sdkFrameworkAidlPath(ctx))
-	case sdkCore:
+	case android.SdkCore:
 		return sdkDep{
 			useModule:        true,
 			bootclasspath:    []string{"core.current.stubs", config.DefaultLambdaStubsLibrary},
 			systemModules:    "core-current-stubs-system-modules",
 			noFrameworksLibs: true,
 		}
-	case sdkModule:
+	case android.SdkModule:
 		// TODO(146757305): provide .apk and .aidl that have more APIs for modules
 		return toModule([]string{"android_module_lib_stubs_current"}, "framework-res", nonUpdatableFrameworkAidlPath(ctx))
-	case sdkSystemServer:
+	case android.SdkSystemServer:
 		// TODO(146757305): provide .apk and .aidl that have more APIs for modules
 		return toModule([]string{"android_system_server_stubs_current"}, "framework-res", sdkFrameworkAidlPath(ctx))
 	default:
-		panic(fmt.Errorf("invalid sdk %q", sdkVersion.raw))
+		panic(fmt.Errorf("invalid sdk %q", sdkVersion.Raw))
 	}
 }