blob: 01b55d0da12ca84304c2dfff11e42bffd364115a [file] [log] [blame]
// 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())
}