|  | // 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 ToSdkKind(s string) SdkKind { | 
|  | for kind := SdkNone; kind <= SdkPrivate; kind++ { | 
|  | if s == kind.String() { | 
|  | return kind | 
|  | } | 
|  | } | 
|  | return SdkInvalid | 
|  | } | 
|  |  | 
|  | 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()) | 
|  | } |