| // Copyright 2021 Google Inc. All rights reserved. | 
 | // | 
 | // Licensed under the Apache License, Version 2.0 (the "License"); | 
 | // you may not use this file except in compliance with the License. | 
 | // You may obtain a copy of the License at | 
 | // | 
 | //     http://www.apache.org/licenses/LICENSE-2.0 | 
 | // | 
 | // Unless required by applicable law or agreed to in writing, software | 
 | // distributed under the License is distributed on an "AS IS" BASIS, | 
 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 | // See the License for the specific language governing permissions and | 
 | // limitations under the License. | 
 |  | 
 | package android | 
 |  | 
 | import ( | 
 | 	"fmt" | 
 | 	"reflect" | 
 | 	"strconv" | 
 | 	"strings" | 
 | ) | 
 |  | 
 | type SdkContext interface { | 
 | 	// SdkVersion returns SdkSpec that corresponds to the sdk_version property of the current module | 
 | 	SdkVersion(ctx EarlyModuleContext) SdkSpec | 
 | 	// SystemModules returns the system_modules property of the current module, or an empty string if it is not set. | 
 | 	SystemModules() string | 
 | 	// MinSdkVersion returns ApiLevel that corresponds to the min_sdk_version property of the current module, | 
 | 	// or from sdk_version if it is not set. | 
 | 	MinSdkVersion(ctx EarlyModuleContext) ApiLevel | 
 | 	// ReplaceMaxSdkVersionPlaceholder returns Apilevel to replace the maxSdkVersion property of permission and | 
 | 	// uses-permission tags if it is set. | 
 | 	ReplaceMaxSdkVersionPlaceholder(ctx EarlyModuleContext) ApiLevel | 
 | 	// TargetSdkVersion returns the ApiLevel that corresponds to the target_sdk_version property of the current module, | 
 | 	// or from sdk_version if it is not set. | 
 | 	TargetSdkVersion(ctx EarlyModuleContext) ApiLevel | 
 | } | 
 |  | 
 | // SdkKind represents a particular category of an SDK spec like public, system, test, etc. | 
 | type SdkKind int | 
 |  | 
 | // These are generally ordered from the narrower sdk version to the wider sdk version, | 
 | // but not all entries have a strict subset/superset relationship. | 
 | // For example, SdkTest and SdkModule do not have a strict subset/superset relationship but both | 
 | // are supersets of SdkSystem. | 
 | // The general trend should be kept when an additional sdk kind is added. | 
 | const ( | 
 | 	SdkInvalid SdkKind = iota | 
 | 	SdkNone | 
 | 	SdkToolchain // API surface provided by ART to compile other API domains | 
 | 	SdkCore | 
 | 	SdkCorePlatform | 
 | 	SdkIntraCore // API surface provided by one core module to another | 
 | 	SdkPublic | 
 | 	SdkSystem | 
 | 	SdkTest | 
 | 	SdkTestFrameworksCore | 
 | 	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 SdkTestFrameworksCore: | 
 | 		return "test_frameworks_core" | 
 | 	case SdkCore: | 
 | 		return "core" | 
 | 	case SdkCorePlatform: | 
 | 		return "core_platform" | 
 | 	case SdkIntraCore: | 
 | 		return "intracore" | 
 | 	case SdkModule: | 
 | 		return "module-lib" | 
 | 	case SdkSystemServer: | 
 | 		return "system-server" | 
 | 	case SdkToolchain: | 
 | 		return "toolchain" | 
 | 	default: | 
 | 		return "invalid" | 
 | 	} | 
 | } | 
 |  | 
 | func (k SdkKind) DefaultJavaLibraryName() string { | 
 | 	switch k { | 
 | 	case SdkPublic: | 
 | 		return "android_stubs_current" | 
 | 	case SdkSystem: | 
 | 		return "android_system_stubs_current" | 
 | 	case SdkTest: | 
 | 		return "android_test_stubs_current" | 
 | 	case SdkTestFrameworksCore: | 
 | 		return "android_test_frameworks_core_stubs_current" | 
 | 	case SdkCore: | 
 | 		return "core.current.stubs" | 
 | 	case SdkModule: | 
 | 		return "android_module_lib_stubs_current" | 
 | 	case SdkSystemServer: | 
 | 		return "android_system_server_stubs_current" | 
 | 	default: | 
 | 		panic(fmt.Errorf("APIs of API surface %v cannot be provided by a single Soong module\n", k)) | 
 | 	} | 
 | } | 
 |  | 
 | func (k SdkKind) DefaultExportableJavaLibraryName() string { | 
 | 	switch k { | 
 | 	case SdkPublic, SdkSystem, SdkTest, SdkModule, SdkSystemServer: | 
 | 		return k.DefaultJavaLibraryName() + "_exportable" | 
 | 	case SdkCore: | 
 | 		return k.DefaultJavaLibraryName() + ".exportable" | 
 | 	default: | 
 | 		panic(fmt.Errorf("API surface %v does not provide exportable stubs", k)) | 
 | 	} | 
 | } | 
 |  | 
 | // SdkSpec represents the kind and the version of an SDK for a module to build against | 
 | type SdkSpec struct { | 
 | 	Kind     SdkKind | 
 | 	ApiLevel ApiLevel | 
 | 	Raw      string | 
 | } | 
 |  | 
 | func (s SdkSpec) String() string { | 
 | 	return fmt.Sprintf("%s_%s", s.Kind, s.ApiLevel) | 
 | } | 
 |  | 
 | // 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, SdkTestFrameworksCore, SdkPrivate: | 
 | 		return false | 
 | 	default: | 
 | 		panic(fmt.Errorf("unknown SdkKind=%v", s.Kind)) | 
 | 	} | 
 | 	return false | 
 | } | 
 |  | 
 | // PrebuiltSdkAvailableForUnbundledBuild 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 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() | 
 | 	// b/314011075: special case for Java modules in vendor partition. They can no longer use | 
 | 	// SDK 35 or later. Their maximum API level is limited to 34 (Android U). This is to | 
 | 	// discourage the use of Java APIs in the vendor partition which hasn't been officially | 
 | 	// supported since the Project Treble back in Android 10. We would like to eventually | 
 | 	// evacuate all Java modules from the partition, but that shall be done progressively. | 
 | 	// Note that the check for the availability of SDK 34 is to not break existing tests where | 
 | 	// any of the frozen SDK version is unavailable. | 
 | 	if isJava(ctx.Module()) && isSdkVersion34AvailableIn(ctx.Config()) { | 
 | 		currentSdkVersion = "34" | 
 | 	} | 
 |  | 
 | 	if currentSdkVersion == "current" { | 
 | 		return s | 
 | 	} | 
 |  | 
 | 	if s.Kind == SdkPublic || s.Kind == SdkSystem { | 
 | 		if s.ApiLevel.IsCurrent() { | 
 | 			if i, err := strconv.Atoi(currentSdkVersion); err == nil { | 
 | 				apiLevel := uncheckedFinalApiLevel(i) | 
 | 				return SdkSpec{s.Kind, apiLevel, 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 EarlyModuleContext) bool { | 
 | 	switch s { | 
 | 	case SdkSpecNone, SdkSpecCorePlatform, SdkSpecPrivate: | 
 | 		return false | 
 | 	} | 
 |  | 
 | 	if s.ApiLevel.IsCurrent() { | 
 | 		// "current" can be built from source and be from prebuilt SDK | 
 | 		return ctx.Config().AlwaysUsePrebuiltSdks() | 
 | 	} else if !s.ApiLevel.IsPreview() { | 
 | 		// validation check | 
 | 		if s.Kind != SdkPublic && s.Kind != SdkSystem && s.Kind != SdkTest && | 
 | 			s.Kind != SdkTestFrameworksCore && s.Kind != SdkModule && s.Kind != SdkSystemServer { | 
 | 			panic(fmt.Errorf("prebuilt SDK is not not available for SdkKind=%q", s.Kind)) | 
 | 			return false | 
 | 		} | 
 | 		// numbered SDKs are always from prebuilt | 
 | 		return true | 
 | 	} | 
 | 	return false | 
 | } | 
 |  | 
 | // EffectiveVersion converts an SdkSpec into the concrete ApiLevel that the module should use. For | 
 | // modules targeting an unreleased SDK (meaning it does not yet have a number) it returns | 
 | // FutureApiLevel(10000). | 
 | func (s SdkSpec) EffectiveVersion(ctx EarlyModuleContext) (ApiLevel, error) { | 
 | 	if !s.Valid() { | 
 | 		return s.ApiLevel, fmt.Errorf("invalid sdk version %q", s.Raw) | 
 | 	} | 
 |  | 
 | 	if ctx.DeviceSpecific() || ctx.SocSpecific() { | 
 | 		s = s.ForVendorPartition(ctx) | 
 | 	} | 
 | 	return s.ApiLevel.EffectiveVersion(ctx) | 
 | } | 
 |  | 
 | // 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 EarlyModuleContext) (string, error) { | 
 | 	if !s.Valid() { | 
 | 		return s.ApiLevel.String(), fmt.Errorf("invalid sdk version %q", s.Raw) | 
 | 	} | 
 |  | 
 | 	if ctx.DeviceSpecific() || ctx.SocSpecific() { | 
 | 		s = s.ForVendorPartition(ctx) | 
 | 	} | 
 | 	return s.ApiLevel.EffectiveVersionString(ctx) | 
 | } | 
 |  | 
 | var ( | 
 | 	SdkSpecNone         = SdkSpec{SdkNone, NoneApiLevel, "(no version)"} | 
 | 	SdkSpecPrivate      = SdkSpec{SdkPrivate, PrivateApiLevel, ""} | 
 | 	SdkSpecCorePlatform = SdkSpec{SdkCorePlatform, FutureApiLevel, "core_platform"} | 
 | ) | 
 |  | 
 | func SdkSpecFrom(ctx EarlyModuleContext, str string) SdkSpec { | 
 | 	return SdkSpecFromWithConfig(ctx.Config(), str) | 
 | } | 
 |  | 
 | func SdkSpecFromWithConfig(config Config, str string) SdkSpec { | 
 | 	switch str { | 
 | 	// special cases first | 
 | 	case "": | 
 | 		return SdkSpecPrivate | 
 | 	case "none": | 
 | 		return SdkSpecNone | 
 | 	case "core_platform": | 
 | 		return SdkSpecCorePlatform | 
 | 	default: | 
 | 		// the syntax is [kind_]version | 
 | 		sep := strings.LastIndex(str, "_") | 
 |  | 
 | 		var kindString string | 
 | 		if sep == 0 { | 
 | 			return SdkSpec{SdkInvalid, NewInvalidApiLevel(str), 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 "test_frameworks_core": | 
 | 			kind = SdkTestFrameworksCore | 
 | 		case "module": | 
 | 			kind = SdkModule | 
 | 		case "system_server": | 
 | 			kind = SdkSystemServer | 
 | 		default: | 
 | 			return SdkSpec{SdkInvalid, NoneApiLevel, str} | 
 | 		} | 
 |  | 
 | 		apiLevel, err := ApiLevelFromUserWithConfig(config, versionString) | 
 | 		if err != nil { | 
 | 			return SdkSpec{SdkInvalid, NewInvalidApiLevel(versionString), str} | 
 | 		} | 
 | 		return SdkSpec{kind, apiLevel, str} | 
 | 	} | 
 | } | 
 |  | 
 | // Checks if the use of this SDK `s` is valid for the given module context `ctx`. | 
 | func (s SdkSpec) ValidateSystemSdk(ctx EarlyModuleContext) bool { | 
 | 	// Do some early checks. This check is currently only for Java modules. And our only concern | 
 | 	// is the use of "system" SDKs. | 
 | 	if !isJava(ctx.Module()) || s.Kind != SdkSystem || ctx.DeviceConfig().BuildBrokenDontCheckSystemSdk() { | 
 | 		return true | 
 | 	} | 
 |  | 
 | 	inVendor := ctx.DeviceSpecific() || ctx.SocSpecific() | 
 | 	inProduct := ctx.ProductSpecific() | 
 | 	isProductUnbundled := ctx.Config().EnforceProductPartitionInterface() | 
 | 	inApex := false | 
 | 	if am, ok := ctx.Module().(ApexModule); ok { | 
 | 		inApex = am.InAnyApex() | 
 | 	} | 
 | 	isUnbundled := inVendor || (inProduct && isProductUnbundled) || inApex | 
 |  | 
 | 	// Bundled modules can use any SDK | 
 | 	if !isUnbundled { | 
 | 		return true | 
 | 	} | 
 |  | 
 | 	// Unbundled modules are allowed to use BOARD_SYSTEMSDK_VERSIONS | 
 | 	supportedVersions := ctx.DeviceConfig().SystemSdkVersions() | 
 |  | 
 | 	// b/314011075: special case for vendor modules. Java modules in the vendor partition can | 
 | 	// not use SDK 35 or later. This is to discourage the use of Java APIs in the vendor | 
 | 	// partition which hasn't been officially supported since the Project Treble back in Android | 
 | 	// 10. We would like to eventually evacuate all Java modules from the partition, but that | 
 | 	// shall be done progressively. | 
 | 	if inVendor { | 
 | 		// 28 was the API when BOARD_SYSTEMSDK_VERSIONS was introduced, so that's the oldest | 
 | 		// we should allow. | 
 | 		supportedVersions = []string{} | 
 | 		for v := 28; v <= 34; v++ { | 
 | 			supportedVersions = append(supportedVersions, strconv.Itoa(v)) | 
 | 		} | 
 | 	} | 
 |  | 
 | 	// APEXes in the system partition are still considered as part of the platform, thus can use | 
 | 	// more SDKs from PLATFORM_SYSTEMSDK_VERSIONS | 
 | 	if inApex && !inVendor { | 
 | 		supportedVersions = ctx.DeviceConfig().PlatformSystemSdkVersions() | 
 | 	} | 
 |  | 
 | 	thisVer, err := s.EffectiveVersion(ctx) | 
 | 	if err != nil { | 
 | 		ctx.PropertyErrorf("sdk_version", "invalid sdk version %q", s.Raw) | 
 | 		return false | 
 | 	} | 
 |  | 
 | 	thisVerString := strconv.Itoa(thisVer.FinalOrPreviewInt()) | 
 | 	if thisVer.IsPreview() { | 
 | 		thisVerString = *ctx.Config().productVariables.Platform_sdk_version_or_codename | 
 | 	} | 
 |  | 
 | 	if !InList(thisVerString, supportedVersions) { | 
 | 		ctx.PropertyErrorf("sdk_version", "incompatible sdk version %q. System SDK version should be one of %q", | 
 | 			s.Raw, supportedVersions) | 
 | 		return false | 
 | 	} | 
 | 	return true | 
 | } | 
 |  | 
 | func isJava(m Module) bool { | 
 | 	moduleType := reflect.TypeOf(m).String() | 
 | 	return strings.HasPrefix(moduleType, "*java.") | 
 | } | 
 |  | 
 | func isSdkVersion34AvailableIn(c Config) bool { | 
 | 	return c.PlatformSdkVersion().FinalInt() >= 34 | 
 | } | 
 |  | 
 | func init() { | 
 | 	RegisterMakeVarsProvider(pctx, javaSdkMakeVars) | 
 | } | 
 |  | 
 | // Export the name of the soong modules representing the various Java API surfaces. | 
 | func javaSdkMakeVars(ctx MakeVarsContext) { | 
 | 	ctx.Strict("ANDROID_PUBLIC_STUBS", SdkPublic.DefaultJavaLibraryName()) | 
 | 	ctx.Strict("ANDROID_PUBLIC_EXPORTABLE_STUBS", SdkPublic.DefaultExportableJavaLibraryName()) | 
 | 	ctx.Strict("ANDROID_SYSTEM_STUBS", SdkSystem.DefaultJavaLibraryName()) | 
 | 	ctx.Strict("ANDROID_TEST_STUBS", SdkTest.DefaultJavaLibraryName()) | 
 | 	ctx.Strict("ANDROID_MODULE_LIB_STUBS", SdkModule.DefaultJavaLibraryName()) | 
 | 	ctx.Strict("ANDROID_SYSTEM_SERVER_STUBS", SdkSystemServer.DefaultJavaLibraryName()) | 
 | 	ctx.Strict("ANDROID_CORE_STUBS", SdkCore.DefaultJavaLibraryName()) | 
 | } |