1// Copyright 2021 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package android
16
17import (
18	"fmt"
19	"strconv"
20	"strings"
21)
22
23type SdkContext interface {
24	// SdkVersion returns SdkSpec that corresponds to the sdk_version property of the current module
25	SdkVersion(ctx EarlyModuleContext) SdkSpec
26	// SystemModules returns the system_modules property of the current module, or an empty string if it is not set.
27	SystemModules() string
28	// MinSdkVersion returns SdkSpec that corresponds to the min_sdk_version property of the current module,
29	// or from sdk_version if it is not set.
30	MinSdkVersion(ctx EarlyModuleContext) SdkSpec
31	// TargetSdkVersion returns the SdkSpec that corresponds to the target_sdk_version property of the current module,
32	// or from sdk_version if it is not set.
33	TargetSdkVersion(ctx EarlyModuleContext) SdkSpec
34}
35
36// SdkKind represents a particular category of an SDK spec like public, system, test, etc.
37type SdkKind int
38
39const (
40	SdkInvalid SdkKind = iota
41	SdkNone
42	SdkCore
43	SdkCorePlatform
44	SdkPublic
45	SdkSystem
46	SdkTest
47	SdkModule
48	SdkSystemServer
49	SdkPrivate
50)
51
52// String returns the string representation of this SdkKind
53func (k SdkKind) String() string {
54	switch k {
55	case SdkPrivate:
56		return "private"
57	case SdkNone:
58		return "none"
59	case SdkPublic:
60		return "public"
61	case SdkSystem:
62		return "system"
63	case SdkTest:
64		return "test"
65	case SdkCore:
66		return "core"
67	case SdkCorePlatform:
68		return "core_platform"
69	case SdkModule:
70		return "module-lib"
71	case SdkSystemServer:
72		return "system-server"
73	default:
74		return "invalid"
75	}
76}
77
78// SdkSpec represents the kind and the version of an SDK for a module to build against
79type SdkSpec struct {
80	Kind     SdkKind
81	ApiLevel ApiLevel
82	Raw      string
83}
84
85func (s SdkSpec) String() string {
86	return fmt.Sprintf("%s_%s", s.Kind, s.ApiLevel)
87}
88
89// Valid checks if this SdkSpec is well-formed. Note however that true doesn't mean that the
90// specified SDK actually exists.
91func (s SdkSpec) Valid() bool {
92	return s.Kind != SdkInvalid
93}
94
95// Specified checks if this SdkSpec is well-formed and is not "".
96func (s SdkSpec) Specified() bool {
97	return s.Valid() && s.Kind != SdkPrivate
98}
99
100// whether the API surface is managed and versioned, i.e. has .txt file that
101// get frozen on SDK freeze and changes get reviewed by API council.
102func (s SdkSpec) Stable() bool {
103	if !s.Specified() {
104		return false
105	}
106	switch s.Kind {
107	case SdkNone:
108		// there is nothing to manage and version in this case; de facto stable API.
109		return true
110	case SdkCore, SdkPublic, SdkSystem, SdkModule, SdkSystemServer:
111		return true
112	case SdkCorePlatform, SdkTest, SdkPrivate:
113		return false
114	default:
115		panic(fmt.Errorf("unknown SdkKind=%v", s.Kind))
116	}
117	return false
118}
119
120// PrebuiltSdkAvailableForUnbundledBuilt tells whether this SdkSpec can have a prebuilt SDK
121// that can be used for unbundled builds.
122func (s SdkSpec) PrebuiltSdkAvailableForUnbundledBuild() bool {
123	// "", "none", and "core_platform" are not available for unbundled build
124	// as we don't/can't have prebuilt stub for the versions
125	return s.Kind != SdkPrivate && s.Kind != SdkNone && s.Kind != SdkCorePlatform
126}
127
128func (s SdkSpec) ForVendorPartition(ctx EarlyModuleContext) SdkSpec {
129	// If BOARD_CURRENT_API_LEVEL_FOR_VENDOR_MODULES has a numeric value,
130	// use it instead of "current" for the vendor partition.
131	currentSdkVersion := ctx.DeviceConfig().CurrentApiLevelForVendorModules()
132	if currentSdkVersion == "current" {
133		return s
134	}
135
136	if s.Kind == SdkPublic || s.Kind == SdkSystem {
137		if s.ApiLevel.IsCurrent() {
138			if i, err := strconv.Atoi(currentSdkVersion); err == nil {
139				apiLevel := uncheckedFinalApiLevel(i)
140				return SdkSpec{s.Kind, apiLevel, s.Raw}
141			}
142			panic(fmt.Errorf("BOARD_CURRENT_API_LEVEL_FOR_VENDOR_MODULES must be either \"current\" or a number, but was %q", currentSdkVersion))
143		}
144	}
145	return s
146}
147
148// UsePrebuilt determines whether prebuilt SDK should be used for this SdkSpec with the given context.
149func (s SdkSpec) UsePrebuilt(ctx EarlyModuleContext) bool {
150	switch s {
151	case SdkSpecNone, SdkSpecCorePlatform, SdkSpecPrivate:
152		return false
153	}
154
155	if s.ApiLevel.IsCurrent() {
156		// "current" can be built from source and be from prebuilt SDK
157		return ctx.Config().AlwaysUsePrebuiltSdks()
158	} else if !s.ApiLevel.IsPreview() {
159		// validation check
160		if s.Kind != SdkPublic && s.Kind != SdkSystem && s.Kind != SdkTest && s.Kind != SdkModule {
161			panic(fmt.Errorf("prebuilt SDK is not not available for SdkKind=%q", s.Kind))
162			return false
163		}
164		// numbered SDKs are always from prebuilt
165		return true
166	}
167	return false
168}
169
170// EffectiveVersion converts an SdkSpec into the concrete ApiLevel that the module should use. For
171// modules targeting an unreleased SDK (meaning it does not yet have a number) it returns
172// FutureApiLevel(10000).
173func (s SdkSpec) EffectiveVersion(ctx EarlyModuleContext) (ApiLevel, error) {
174	if !s.Valid() {
175		return s.ApiLevel, fmt.Errorf("invalid sdk version %q", s.Raw)
176	}
177
178	if ctx.DeviceSpecific() || ctx.SocSpecific() {
179		s = s.ForVendorPartition(ctx)
180	}
181	if !s.ApiLevel.IsPreview() {
182		return s.ApiLevel, nil
183	}
184	ret := ctx.Config().DefaultAppTargetSdk(ctx)
185	if ret.IsPreview() {
186		return FutureApiLevel, nil
187	}
188	return ret, nil
189}
190
191// EffectiveVersionString converts an SdkSpec into the concrete version string that the module
192// should use. For modules targeting an unreleased SDK (meaning it does not yet have a number)
193// it returns the codename (P, Q, R, etc.)
194func (s SdkSpec) EffectiveVersionString(ctx EarlyModuleContext) (string, error) {
195	if !s.Valid() {
196		return s.ApiLevel.String(), fmt.Errorf("invalid sdk version %q", s.Raw)
197	}
198
199	if ctx.DeviceSpecific() || ctx.SocSpecific() {
200		s = s.ForVendorPartition(ctx)
201	}
202	if !s.ApiLevel.IsPreview() {
203		return s.ApiLevel.String(), nil
204	}
205	return ctx.Config().DefaultAppTargetSdk(ctx).String(), nil
206}
207
208var (
209	SdkSpecNone         = SdkSpec{SdkNone, NoneApiLevel, "(no version)"}
210	SdkSpecPrivate      = SdkSpec{SdkPrivate, FutureApiLevel, ""}
211	SdkSpecCorePlatform = SdkSpec{SdkCorePlatform, FutureApiLevel, "core_platform"}
212)
213
214func SdkSpecFrom(ctx EarlyModuleContext, str string) SdkSpec {
215	switch str {
216	// special cases first
217	case "":
218		return SdkSpecPrivate
219	case "none":
220		return SdkSpecNone
221	case "core_platform":
222		return SdkSpecCorePlatform
223	default:
224		// the syntax is [kind_]version
225		sep := strings.LastIndex(str, "_")
226
227		var kindString string
228		if sep == 0 {
229			return SdkSpec{SdkInvalid, NoneApiLevel, str}
230		} else if sep == -1 {
231			kindString = ""
232		} else {
233			kindString = str[0:sep]
234		}
235		versionString := str[sep+1 : len(str)]
236
237		var kind SdkKind
238		switch kindString {
239		case "":
240			kind = SdkPublic
241		case "core":
242			kind = SdkCore
243		case "system":
244			kind = SdkSystem
245		case "test":
246			kind = SdkTest
247		case "module":
248			kind = SdkModule
249		case "system_server":
250			kind = SdkSystemServer
251		default:
252			return SdkSpec{SdkInvalid, NoneApiLevel, str}
253		}
254
255		apiLevel, err := ApiLevelFromUser(ctx, versionString)
256		if err != nil {
257			return SdkSpec{SdkInvalid, apiLevel, str}
258		}
259		return SdkSpec{kind, apiLevel, str}
260	}
261}
262
263func (s SdkSpec) ValidateSystemSdk(ctx EarlyModuleContext) bool {
264	// Ensures that the specified system SDK version is one of BOARD_SYSTEMSDK_VERSIONS (for vendor/product Java module)
265	// Assuming that BOARD_SYSTEMSDK_VERSIONS := 28 29,
266	// sdk_version of the modules in vendor/product that use system sdk must be either system_28, system_29 or system_current
267	if s.Kind != SdkSystem || s.ApiLevel.IsPreview() {
268		return true
269	}
270	allowedVersions := ctx.DeviceConfig().PlatformSystemSdkVersions()
271	if ctx.DeviceSpecific() || ctx.SocSpecific() || (ctx.ProductSpecific() && ctx.Config().EnforceProductPartitionInterface()) {
272		systemSdkVersions := ctx.DeviceConfig().SystemSdkVersions()
273		if len(systemSdkVersions) > 0 {
274			allowedVersions = systemSdkVersions
275		}
276	}
277	if len(allowedVersions) > 0 && !InList(s.ApiLevel.String(), allowedVersions) {
278		ctx.PropertyErrorf("sdk_version", "incompatible sdk version %q. System SDK version should be one of %q",
279			s.Raw, allowedVersions)
280		return false
281	}
282	return true
283}
284