1// Copyright 2019 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 java
16
17import (
18	"fmt"
19	"path/filepath"
20	"sort"
21	"strconv"
22
23	"android/soong/android"
24	"android/soong/java/config"
25
26	"github.com/google/blueprint/pathtools"
27)
28
29func init() {
30	android.RegisterPreSingletonType("sdk_versions", sdkPreSingletonFactory)
31	android.RegisterSingletonType("sdk", sdkSingletonFactory)
32	android.RegisterMakeVarsProvider(pctx, sdkMakeVars)
33}
34
35var sdkVersionsKey = android.NewOnceKey("sdkVersionsKey")
36var sdkFrameworkAidlPathKey = android.NewOnceKey("sdkFrameworkAidlPathKey")
37var nonUpdatableFrameworkAidlPathKey = android.NewOnceKey("nonUpdatableFrameworkAidlPathKey")
38var apiFingerprintPathKey = android.NewOnceKey("apiFingerprintPathKey")
39
40func UseApiFingerprint(ctx android.BaseModuleContext) bool {
41	if ctx.Config().UnbundledBuild() &&
42		!ctx.Config().AlwaysUsePrebuiltSdks() &&
43		ctx.Config().IsEnvTrue("UNBUNDLED_BUILD_TARGET_SDK_WITH_API_FINGERPRINT") {
44		return true
45	}
46	return false
47}
48
49func defaultJavaLanguageVersion(ctx android.EarlyModuleContext, s android.SdkSpec) javaVersion {
50	sdk, err := s.EffectiveVersion(ctx)
51	if err != nil {
52		ctx.PropertyErrorf("sdk_version", "%s", err)
53	}
54	if sdk.FinalOrFutureInt() <= 23 {
55		return JAVA_VERSION_7
56	} else if sdk.FinalOrFutureInt() <= 29 {
57		return JAVA_VERSION_8
58	} else {
59		return JAVA_VERSION_9
60	}
61}
62
63func decodeSdkDep(ctx android.EarlyModuleContext, sdkContext android.SdkContext) sdkDep {
64	sdkVersion := sdkContext.SdkVersion(ctx)
65	if !sdkVersion.Valid() {
66		ctx.PropertyErrorf("sdk_version", "invalid version %q", sdkVersion.Raw)
67		return sdkDep{}
68	}
69
70	if ctx.DeviceSpecific() || ctx.SocSpecific() {
71		sdkVersion = sdkVersion.ForVendorPartition(ctx)
72	}
73
74	if !sdkVersion.ValidateSystemSdk(ctx) {
75		return sdkDep{}
76	}
77
78	if sdkVersion.UsePrebuilt(ctx) {
79		dir := filepath.Join("prebuilts", "sdk", sdkVersion.ApiLevel.String(), sdkVersion.Kind.String())
80		jar := filepath.Join(dir, "android.jar")
81		// There's no aidl for other SDKs yet.
82		// TODO(77525052): Add aidl files for other SDKs too.
83		publicDir := filepath.Join("prebuilts", "sdk", sdkVersion.ApiLevel.String(), "public")
84		aidl := filepath.Join(publicDir, "framework.aidl")
85		jarPath := android.ExistentPathForSource(ctx, jar)
86		aidlPath := android.ExistentPathForSource(ctx, aidl)
87		lambdaStubsPath := android.PathForSource(ctx, config.SdkLambdaStubsPath)
88
89		if (!jarPath.Valid() || !aidlPath.Valid()) && ctx.Config().AllowMissingDependencies() {
90			return sdkDep{
91				invalidVersion: true,
92				bootclasspath:  []string{fmt.Sprintf("sdk_%s_%s_android", sdkVersion.Kind, sdkVersion.ApiLevel.String())},
93			}
94		}
95
96		if !jarPath.Valid() {
97			ctx.PropertyErrorf("sdk_version", "invalid sdk version %q, %q does not exist", sdkVersion.Raw, jar)
98			return sdkDep{}
99		}
100
101		if !aidlPath.Valid() {
102			ctx.PropertyErrorf("sdk_version", "invalid sdk version %q, %q does not exist", sdkVersion.Raw, aidl)
103			return sdkDep{}
104		}
105
106		var systemModules string
107		if defaultJavaLanguageVersion(ctx, sdkVersion).usesJavaModules() {
108			systemModules = "sdk_public_" + sdkVersion.ApiLevel.String() + "_system_modules"
109		}
110
111		return sdkDep{
112			useFiles:      true,
113			jars:          android.Paths{jarPath.Path(), lambdaStubsPath},
114			aidl:          android.OptionalPathForPath(aidlPath.Path()),
115			systemModules: systemModules,
116		}
117	}
118
119	toModule := func(modules []string, res string, aidl android.Path) sdkDep {
120		return sdkDep{
121			useModule:          true,
122			bootclasspath:      append(modules, config.DefaultLambdaStubsLibrary),
123			systemModules:      "core-current-stubs-system-modules",
124			java9Classpath:     modules,
125			frameworkResModule: res,
126			aidl:               android.OptionalPathForPath(aidl),
127		}
128	}
129
130	switch sdkVersion.Kind {
131	case android.SdkPrivate:
132		return sdkDep{
133			useModule:          true,
134			systemModules:      corePlatformSystemModules(ctx),
135			bootclasspath:      corePlatformBootclasspathLibraries(ctx),
136			classpath:          config.FrameworkLibraries,
137			frameworkResModule: "framework-res",
138		}
139	case android.SdkNone:
140		systemModules := sdkContext.SystemModules()
141		if systemModules == "" {
142			ctx.PropertyErrorf("sdk_version",
143				`system_modules is required to be set to a non-empty value when sdk_version is "none", did you mean sdk_version: "core_platform"?`)
144		} else if systemModules == "none" {
145			return sdkDep{
146				noStandardLibs: true,
147			}
148		}
149
150		return sdkDep{
151			useModule:      true,
152			noStandardLibs: true,
153			systemModules:  systemModules,
154			bootclasspath:  []string{systemModules},
155		}
156	case android.SdkCorePlatform:
157		return sdkDep{
158			useModule:        true,
159			systemModules:    corePlatformSystemModules(ctx),
160			bootclasspath:    corePlatformBootclasspathLibraries(ctx),
161			noFrameworksLibs: true,
162		}
163	case android.SdkPublic:
164		return toModule([]string{"android_stubs_current"}, "framework-res", sdkFrameworkAidlPath(ctx))
165	case android.SdkSystem:
166		return toModule([]string{"android_system_stubs_current"}, "framework-res", sdkFrameworkAidlPath(ctx))
167	case android.SdkTest:
168		return toModule([]string{"android_test_stubs_current"}, "framework-res", sdkFrameworkAidlPath(ctx))
169	case android.SdkCore:
170		return sdkDep{
171			useModule:        true,
172			bootclasspath:    []string{"core.current.stubs", config.DefaultLambdaStubsLibrary},
173			systemModules:    "core-current-stubs-system-modules",
174			noFrameworksLibs: true,
175		}
176	case android.SdkModule:
177		// TODO(146757305): provide .apk and .aidl that have more APIs for modules
178		return sdkDep{
179			useModule:          true,
180			bootclasspath:      []string{"android_module_lib_stubs_current", config.DefaultLambdaStubsLibrary},
181			systemModules:      "core-module-lib-stubs-system-modules",
182			java9Classpath:     []string{"android_module_lib_stubs_current"},
183			frameworkResModule: "framework-res",
184			aidl:               android.OptionalPathForPath(nonUpdatableFrameworkAidlPath(ctx)),
185		}
186	case android.SdkSystemServer:
187		// TODO(146757305): provide .apk and .aidl that have more APIs for modules
188		return sdkDep{
189			useModule:          true,
190			bootclasspath:      []string{"android_system_server_stubs_current", config.DefaultLambdaStubsLibrary},
191			systemModules:      "core-module-lib-stubs-system-modules",
192			java9Classpath:     []string{"android_system_server_stubs_current"},
193			frameworkResModule: "framework-res",
194			aidl:               android.OptionalPathForPath(sdkFrameworkAidlPath(ctx)),
195		}
196	default:
197		panic(fmt.Errorf("invalid sdk %q", sdkVersion.Raw))
198	}
199}
200
201func sdkPreSingletonFactory() android.Singleton {
202	return sdkPreSingleton{}
203}
204
205type sdkPreSingleton struct{}
206
207func (sdkPreSingleton) GenerateBuildActions(ctx android.SingletonContext) {
208	sdkJars, err := ctx.GlobWithDeps("prebuilts/sdk/*/public/android.jar", nil)
209	if err != nil {
210		ctx.Errorf("failed to glob prebuilts/sdk/*/public/android.jar: %s", err.Error())
211	}
212
213	var sdkVersions []int
214	for _, sdkJar := range sdkJars {
215		dir := filepath.Base(filepath.Dir(filepath.Dir(sdkJar)))
216		v, err := strconv.Atoi(dir)
217		if scerr, ok := err.(*strconv.NumError); ok && scerr.Err == strconv.ErrSyntax {
218			continue
219		} else if err != nil {
220			ctx.Errorf("invalid sdk jar %q, %s, %v", sdkJar, err.Error())
221		}
222		sdkVersions = append(sdkVersions, v)
223	}
224
225	sort.Ints(sdkVersions)
226
227	ctx.Config().Once(sdkVersionsKey, func() interface{} { return sdkVersions })
228}
229
230func LatestSdkVersionInt(ctx android.EarlyModuleContext) int {
231	sdkVersions := ctx.Config().Get(sdkVersionsKey).([]int)
232	latestSdkVersion := 0
233	if len(sdkVersions) > 0 {
234		latestSdkVersion = sdkVersions[len(sdkVersions)-1]
235	}
236	return latestSdkVersion
237}
238
239func sdkSingletonFactory() android.Singleton {
240	return sdkSingleton{}
241}
242
243type sdkSingleton struct{}
244
245func (sdkSingleton) GenerateBuildActions(ctx android.SingletonContext) {
246	if ctx.Config().AlwaysUsePrebuiltSdks() {
247		return
248	}
249
250	createSdkFrameworkAidl(ctx)
251	createNonUpdatableFrameworkAidl(ctx)
252	createAPIFingerprint(ctx)
253}
254
255// Create framework.aidl by extracting anything that implements android.os.Parcelable from the SDK stubs modules.
256func createSdkFrameworkAidl(ctx android.SingletonContext) {
257	stubsModules := []string{
258		"android_stubs_current",
259		"android_test_stubs_current",
260		"android_system_stubs_current",
261	}
262
263	combinedAidl := sdkFrameworkAidlPath(ctx)
264	tempPath := tempPathForRestat(ctx, combinedAidl)
265
266	rule := createFrameworkAidl(stubsModules, tempPath, ctx)
267
268	commitChangeForRestat(rule, tempPath, combinedAidl)
269
270	rule.Build("framework_aidl", "generate framework.aidl")
271}
272
273// Creates a version of framework.aidl for the non-updatable part of the platform.
274func createNonUpdatableFrameworkAidl(ctx android.SingletonContext) {
275	stubsModules := []string{"android_module_lib_stubs_current"}
276
277	combinedAidl := nonUpdatableFrameworkAidlPath(ctx)
278	tempPath := tempPathForRestat(ctx, combinedAidl)
279
280	rule := createFrameworkAidl(stubsModules, tempPath, ctx)
281
282	commitChangeForRestat(rule, tempPath, combinedAidl)
283
284	rule.Build("framework_non_updatable_aidl", "generate framework_non_updatable.aidl")
285}
286
287func createFrameworkAidl(stubsModules []string, path android.WritablePath, ctx android.SingletonContext) *android.RuleBuilder {
288	stubsJars := make([]android.Paths, len(stubsModules))
289
290	ctx.VisitAllModules(func(module android.Module) {
291		// Collect dex jar paths for the modules listed above.
292		if ctx.ModuleHasProvider(module, JavaInfoProvider) {
293			j := ctx.ModuleProvider(module, JavaInfoProvider).(JavaInfo)
294			name := ctx.ModuleName(module)
295			if i := android.IndexList(name, stubsModules); i != -1 {
296				stubsJars[i] = j.HeaderJars
297			}
298		}
299	})
300
301	var missingDeps []string
302
303	for i := range stubsJars {
304		if stubsJars[i] == nil {
305			if ctx.Config().AllowMissingDependencies() {
306				missingDeps = append(missingDeps, stubsModules[i])
307			} else {
308				ctx.Errorf("failed to find dex jar path for module %q", stubsModules[i])
309			}
310		}
311	}
312
313	rule := android.NewRuleBuilder(pctx, ctx)
314	rule.MissingDeps(missingDeps)
315
316	var aidls android.Paths
317	for _, jars := range stubsJars {
318		for _, jar := range jars {
319			aidl := android.PathForOutput(ctx, "aidl", pathtools.ReplaceExtension(jar.Base(), "aidl"))
320
321			rule.Command().
322				Text("rm -f").Output(aidl)
323			rule.Command().
324				BuiltTool("sdkparcelables").
325				Input(jar).
326				Output(aidl)
327
328			aidls = append(aidls, aidl)
329		}
330	}
331
332	rule.Command().
333		Text("rm -f").Output(path)
334	rule.Command().
335		Text("cat").
336		Inputs(aidls).
337		Text("| sort -u >").
338		Output(path)
339
340	return rule
341}
342
343func sdkFrameworkAidlPath(ctx android.PathContext) android.OutputPath {
344	return ctx.Config().Once(sdkFrameworkAidlPathKey, func() interface{} {
345		return android.PathForOutput(ctx, "framework.aidl")
346	}).(android.OutputPath)
347}
348
349func nonUpdatableFrameworkAidlPath(ctx android.PathContext) android.OutputPath {
350	return ctx.Config().Once(nonUpdatableFrameworkAidlPathKey, func() interface{} {
351		return android.PathForOutput(ctx, "framework_non_updatable.aidl")
352	}).(android.OutputPath)
353}
354
355// Create api_fingerprint.txt
356func createAPIFingerprint(ctx android.SingletonContext) {
357	out := ApiFingerprintPath(ctx)
358
359	rule := android.NewRuleBuilder(pctx, ctx)
360
361	rule.Command().
362		Text("rm -f").Output(out)
363	cmd := rule.Command()
364
365	if ctx.Config().PlatformSdkCodename() == "REL" {
366		cmd.Text("echo REL >").Output(out)
367	} else if ctx.Config().FrameworksBaseDirExists(ctx) && !ctx.Config().AlwaysUsePrebuiltSdks() {
368		cmd.Text("cat")
369		apiTxtFileModules := []string{
370			"frameworks-base-api-current.txt",
371			"frameworks-base-api-system-current.txt",
372			"frameworks-base-api-module-lib-current.txt",
373			"services-system-server-current.txt",
374		}
375		count := 0
376		ctx.VisitAllModules(func(module android.Module) {
377			name := ctx.ModuleName(module)
378			if android.InList(name, apiTxtFileModules) {
379				cmd.Inputs(android.OutputFilesForModule(ctx, module, ""))
380				count++
381			}
382		})
383		if count != len(apiTxtFileModules) {
384			ctx.Errorf("Could not find all the expected API modules %v, found %d\n", apiTxtFileModules, count)
385			return
386		}
387		cmd.Text("| md5sum | cut -d' ' -f1 >").
388			Output(out)
389	} else {
390		// Unbundled build
391		// TODO: use a prebuilt api_fingerprint.txt from prebuilts/sdk/current.txt once we have one
392		cmd.Text("echo").
393			Flag(ctx.Config().PlatformPreviewSdkVersion()).
394			Text(">").
395			Output(out)
396	}
397
398	rule.Build("api_fingerprint", "generate api_fingerprint.txt")
399}
400
401func ApiFingerprintPath(ctx android.PathContext) android.OutputPath {
402	return ctx.Config().Once(apiFingerprintPathKey, func() interface{} {
403		return android.PathForOutput(ctx, "api_fingerprint.txt")
404	}).(android.OutputPath)
405}
406
407func sdkMakeVars(ctx android.MakeVarsContext) {
408	if ctx.Config().AlwaysUsePrebuiltSdks() {
409		return
410	}
411
412	ctx.Strict("FRAMEWORK_AIDL", sdkFrameworkAidlPath(ctx).String())
413	ctx.Strict("API_FINGERPRINT", ApiFingerprintPath(ctx).String())
414}
415