Replace ApiStrToNum uses with ApiLevel.

Test: treehugger
Bug: http://b/154667674
Change-Id: I2954bb21c1cfdeb305f25cfb6c8711c930f6ed50
diff --git a/android/apex.go b/android/apex.go
index f857ec6..3cc663b 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -24,29 +24,36 @@
 	"github.com/google/blueprint"
 )
 
-const (
-	SdkVersion_Android10 = 29
+var (
+	SdkVersion_Android10 = uncheckedFinalApiLevel(29)
 )
 
 type ApexInfo struct {
 	// Name of the apex variation that this module is mutated into
 	ApexVariationName string
 
-	MinSdkVersion int
-	Updatable     bool
-	RequiredSdks  SdkRefs
+	// Serialized ApiLevel. Use via MinSdkVersion() method. Cannot be stored in
+	// its struct form because this is cloned into properties structs, and
+	// ApiLevel has private members.
+	MinSdkVersionStr string
+	Updatable        bool
+	RequiredSdks     SdkRefs
 
 	InApexes []string
 }
 
-func (i ApexInfo) mergedName() string {
-	name := "apex" + strconv.Itoa(i.MinSdkVersion)
+func (i ApexInfo) mergedName(ctx EarlyModuleContext) string {
+	name := "apex" + strconv.Itoa(i.MinSdkVersion(ctx).FinalOrFutureInt())
 	for _, sdk := range i.RequiredSdks {
 		name += "_" + sdk.Name + "_" + sdk.Version
 	}
 	return name
 }
 
+func (this *ApexInfo) MinSdkVersion(ctx EarlyModuleContext) ApiLevel {
+	return ApiLevelOrPanic(ctx, this.MinSdkVersionStr)
+}
+
 // Extracted from ApexModule to make it easier to define custom subsets of the
 // ApexModule interface and improve code navigation within the IDE.
 type DepIsInSameApex interface {
@@ -141,7 +148,7 @@
 
 	// Returns nil if this module supports sdkVersion
 	// Otherwise, returns error with reason
-	ShouldSupportSdkVersion(ctx BaseModuleContext, sdkVersion int) error
+	ShouldSupportSdkVersion(ctx BaseModuleContext, sdkVersion ApiLevel) error
 
 	// Returns true if this module needs a unique variation per apex, for example if
 	// use_apex_name_macro is set.
@@ -347,18 +354,18 @@
 // mergeApexVariations deduplicates APEX variations that would build identically into a common
 // variation.  It returns the reduced list of variations and a list of aliases from the original
 // variation names to the new variation names.
-func mergeApexVariations(apexVariations []ApexInfo) (merged []ApexInfo, aliases [][2]string) {
+func mergeApexVariations(ctx EarlyModuleContext, apexVariations []ApexInfo) (merged []ApexInfo, aliases [][2]string) {
 	sort.Sort(byApexName(apexVariations))
 	seen := make(map[string]int)
 	for _, apexInfo := range apexVariations {
 		apexName := apexInfo.ApexVariationName
-		mergedName := apexInfo.mergedName()
+		mergedName := apexInfo.mergedName(ctx)
 		if index, exists := seen[mergedName]; exists {
 			merged[index].InApexes = append(merged[index].InApexes, apexName)
 			merged[index].Updatable = merged[index].Updatable || apexInfo.Updatable
 		} else {
 			seen[mergedName] = len(merged)
-			apexInfo.ApexVariationName = apexInfo.mergedName()
+			apexInfo.ApexVariationName = apexInfo.mergedName(ctx)
 			apexInfo.InApexes = CopyOf(apexInfo.InApexes)
 			merged = append(merged, apexInfo)
 		}
@@ -374,7 +381,7 @@
 		var apexVariations []ApexInfo
 		var aliases [][2]string
 		if !mctx.Module().(ApexModule).UniqueApexVariations() && !m.ApexProperties.UniqueApexVariationsForDeps {
-			apexVariations, aliases = mergeApexVariations(m.apexVariations)
+			apexVariations, aliases = mergeApexVariations(mctx, m.apexVariations)
 		} else {
 			apexVariations = m.apexVariations
 		}
@@ -603,7 +610,13 @@
 }
 
 // TODO(b/158059172): remove minSdkVersion allowlist
-var minSdkVersionAllowlist = map[string]int{
+var minSdkVersionAllowlist = func(apiMap map[string]int) map[string]ApiLevel {
+	list := make(map[string]ApiLevel, len(apiMap))
+	for name, finalApiInt := range apiMap {
+		list[name] = uncheckedFinalApiLevel(finalApiInt)
+	}
+	return list
+}(map[string]int{
 	"adbd":                  30,
 	"android.net.ipsec.ike": 30,
 	"androidx-constraintlayout_constraintlayout-solver": 30,
@@ -672,7 +685,7 @@
 	"statsd":                                            30,
 	"tensorflow_headers":                                30,
 	"xz-java":                                           29,
-}
+})
 
 // Function called while walking an APEX's payload dependencies.
 //
@@ -686,7 +699,7 @@
 }
 
 // CheckMinSdkVersion checks if every dependency of an updatable module sets min_sdk_version accordingly
-func CheckMinSdkVersion(m UpdatableModule, ctx ModuleContext, minSdkVersion int) {
+func CheckMinSdkVersion(m UpdatableModule, ctx ModuleContext, minSdkVersion ApiLevel) {
 	// do not enforce min_sdk_version for host
 	if ctx.Host() {
 		return
@@ -699,7 +712,7 @@
 
 	// do not enforce deps.min_sdk_version if APEX/APK doesn't set min_sdk_version or
 	// min_sdk_version is not finalized (e.g. current or codenames)
-	if minSdkVersion == FutureApiLevel {
+	if minSdkVersion.IsCurrent() {
 		return
 	}
 
@@ -714,7 +727,7 @@
 		}
 		if err := to.ShouldSupportSdkVersion(ctx, minSdkVersion); err != nil {
 			toName := ctx.OtherModuleName(to)
-			if ver, ok := minSdkVersionAllowlist[toName]; !ok || ver > minSdkVersion {
+			if ver, ok := minSdkVersionAllowlist[toName]; !ok || ver.GreaterThan(minSdkVersion) {
 				ctx.OtherModuleErrorf(to, "should support min_sdk_version(%v) for %q: %v. Dependency path: %s",
 					minSdkVersion, ctx.ModuleName(), err.Error(), ctx.GetPathString(false))
 				return false
diff --git a/android/api_levels.go b/android/api_levels.go
index ddcdbb7..81f5db0 100644
--- a/android/api_levels.go
+++ b/android/api_levels.go
@@ -49,6 +49,14 @@
 	isPreview bool
 }
 
+func (this ApiLevel) FinalOrFutureInt() int {
+	if this.IsPreview() {
+		return FutureApiLevel
+	} else {
+		return this.number
+	}
+}
+
 // Returns the canonical name for this API level. For a finalized API level
 // this will be the API number as a string. For a preview API level this
 // will be the codename, or "current".
@@ -261,6 +269,17 @@
 			"R":     30,
 		}
 
+		// TODO: Differentiate "current" and "future".
+		// The code base calls it FutureApiLevel, but the spelling is "current",
+		// and these are really two different things. When defining APIs it
+		// means the API has not yet been added to a specific release. When
+		// choosing an API level to build for it means that the future API level
+		// should be used, except in the case where the build is finalized in
+		// which case the platform version should be used. This is *weird*,
+		// because in the circumstance where API foo was added in R and bar was
+		// added in S, both of these are usable when building for "current" when
+		// neither R nor S are final, but the S APIs stop being available in a
+		// final R build.
 		if Bool(config.productVariables.Platform_sdk_final) {
 			apiLevelsMap["current"] = config.PlatformSdkVersionInt()
 		}
@@ -300,24 +319,6 @@
 	}).(map[string]int)
 }
 
-// Converts an API level string into its numeric form.
-// * Codenames are decoded.
-// * Numeric API levels are simply converted.
-// * "current" is mapped to FutureApiLevel(10000)
-// * "minimum" is NDK specific and not handled with this. (refer normalizeNdkApiLevel in cc.go)
-func ApiStrToNum(ctx BaseModuleContext, apiLevel string) (int, error) {
-	if apiLevel == "current" {
-		return FutureApiLevel, nil
-	}
-	if num, ok := getApiLevelsMap(ctx.Config())[apiLevel]; ok {
-		return num, nil
-	}
-	if num, err := strconv.Atoi(apiLevel); err == nil {
-		return num, nil
-	}
-	return 0, fmt.Errorf("SDK version should be one of \"current\", <number> or <codename>: %q", apiLevel)
-}
-
 func (a *apiLevelsSingleton) GenerateBuildActions(ctx SingletonContext) {
 	apiLevelsMap := getApiLevelsMap(ctx.Config())
 	apiLevelsJson := GetApiLevelsJson(ctx)