• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2024 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"
20	"strings"
21
22	"android/soong/android"
23	"android/soong/etc"
24
25	"github.com/google/blueprint/proptools"
26)
27
28// ---------------------------------------------------------------------------------------------
29// Naming scheme of the submodules generated by java_sdk_library and java_sdk_library_import
30// ---------------------------------------------------------------------------------------------
31
32const (
33	sdkXmlFileSuffix = ".xml"
34	implLibSuffix    = ".impl"
35)
36
37func implLibraryModuleName(sdkLibName string) string {
38	return sdkLibName + implLibSuffix
39}
40
41// Module name of the runtime implementation library
42func (c *commonToSdkLibraryAndImport) implLibraryModuleName() string {
43	return implLibraryModuleName(c.module.RootLibraryName())
44}
45
46// Module name of the XML file for the lib
47func (c *commonToSdkLibraryAndImport) xmlPermissionsModuleName() string {
48	return c.module.RootLibraryName() + sdkXmlFileSuffix
49}
50
51// Name of the java_library module that compiles the stubs source.
52func (c *commonToSdkLibraryAndImport) stubsLibraryModuleName(apiScope *apiScope) string {
53	baseName := c.module.RootLibraryName()
54	return apiScope.stubsLibraryModuleName(baseName)
55}
56
57// Name of the java_library module that compiles the exportable stubs source.
58func (c *commonToSdkLibraryAndImport) exportableStubsLibraryModuleName(apiScope *apiScope) string {
59	baseName := c.module.RootLibraryName()
60	return apiScope.exportableStubsLibraryModuleName(baseName)
61}
62
63// Name of the droidstubs module that generates the stubs source and may also
64// generate/check the API.
65func (c *commonToSdkLibraryAndImport) droidstubsModuleName(apiScope *apiScope) string {
66	baseName := c.module.RootLibraryName()
67	return apiScope.stubsSourceModuleName(baseName)
68}
69
70// Name of the java_api_library module that generates the from-text stubs source
71// and compiles to a jar file.
72func (c *commonToSdkLibraryAndImport) fromTextStubsLibraryModuleName(apiScope *apiScope) string {
73	baseName := c.module.RootLibraryName()
74	return apiScope.apiLibraryModuleName(baseName)
75}
76
77// Name of the java_library module that compiles the stubs
78// generated from source Java files.
79func (c *commonToSdkLibraryAndImport) fromSourceStubsLibraryModuleName(apiScope *apiScope) string {
80	baseName := c.module.RootLibraryName()
81	return apiScope.sourceStubsLibraryModuleName(baseName)
82}
83
84// Name of the java_library module that compiles the exportable stubs
85// generated from source Java files.
86func (c *commonToSdkLibraryAndImport) exportableFromSourceStubsLibraryModuleName(apiScope *apiScope) string {
87	baseName := c.module.RootLibraryName()
88	return apiScope.exportableSourceStubsLibraryModuleName(baseName)
89}
90
91// ---------------------------------------------------------------------------------------------
92// Build rules of the submodules generated by java_sdk_library.
93// java_sdk_library "framework-foo" generates the following submodules:
94//
95// - "framework-foo.impl" (type: [Library]): the implementation library, which generates the
96//		compilation outputs that include the implementation details and the private apis
97//		(i.e. class/methods that are annotated @hide).
98//
99// - "framework-foo.stubs.source.<[apiScope.name]>" (type: [Droidstubs]): droidstubs module that
100//		generates the stubs and the api files for the given api scope.
101//
102// - "framework-foo.stubs.<[apiScope.name]>" (type: [Library]): stub library module that
103//		provides the compilation output of the stubs to the reverse dependencies. The module
104//		itself does not perform any compilation actions; the module statically depends on one of
105//		the from-source stub module or the from-text stub configuration based on the build
106// 		configuration.
107//
108// - "framework-foo.stubs.<[apiScope.name]>.from-source" (type: [Library]): stub library module
109//		that compiles the stubs generated by the droidstubs submodule. This module is a static
110//		dependency of the stub library module when
111//		[android/soong/android/config.BuildFromTextStub()] is false.
112//
113// - "framework-foo.stubs.<[apiScope.name]>.from-text" (type: [ApiLibrary]): api library module
114//		that generates and compiles the stubs from the api files checked in the tree instead of
115//		the source Java files (e.g. *-current.txt files). This module is a static dependency of
116//		the stub library module when [android/soong/android/config.BuildFromTextStub()] is true.
117//
118// - "framework-foo.stubs.exportable.<[apiScope.name]>" (type: [Library]): stub library module
119//		that provides the "exportable" stubs. "exportable" stubs are the stubs that do not
120//		include in-development flagged apis. This module is only used for SDK builds to generate
121//		the SDK artifacts, and not purposed for consumption for other modules.
122//
123// - "framework-foo.stubs.exportable.<[apiScope.name]>.from-source" (type: [Library]): stub
124//		library module that compiles the "exportable" stubs generated by the droidstubs
125//		submodule. This module is always a static dependency of the "exportable" stub library
126//		module given that from-text stubs cannot be used for SDK builds as it does not contain
127//		documentations.
128//
129// - "framework-foo.xml" (type: [sdkLibraryXml]): xml library that generates the permission xml
130//		file, which allows [SdkLibrary] to be used with <uses-permission> tag in the
131//		AndroidManifest.xml files.
132// ---------------------------------------------------------------------------------------------
133
134// Creates the implementation [Library] with ".impl" suffix.
135func (module *SdkLibrary) createImplLibrary(mctx android.DefaultableHookContext) {
136	visibility := childModuleVisibility(module.sdkLibraryProperties.Impl_library_visibility)
137
138	staticLibs := module.properties.Static_libs.Clone()
139	staticLibs.AppendSimpleValue(module.sdkLibraryProperties.Impl_only_static_libs)
140	props := struct {
141		Name           *string
142		Enabled        proptools.Configurable[bool]
143		Visibility     []string
144		Libs           []string
145		Static_libs    proptools.Configurable[[]string]
146		Apex_available []string
147		Stem           *string
148	}{
149		Name:       proptools.StringPtr(module.implLibraryModuleName()),
150		Enabled:    module.EnabledProperty(),
151		Visibility: visibility,
152
153		Libs: append(module.properties.Libs, module.sdkLibraryProperties.Impl_only_libs...),
154
155		Static_libs: staticLibs,
156		// Pass the apex_available settings down so that the impl library can be statically
157		// embedded within a library that is added to an APEX. Needed for updatable-media.
158		Apex_available: module.ApexAvailable(),
159
160		Stem: proptools.StringPtr(module.Name()),
161	}
162
163	properties := []interface{}{
164		&module.properties,
165		&module.protoProperties,
166		&module.deviceProperties,
167		&module.dexProperties,
168		&module.dexpreoptProperties,
169		&module.linter.properties,
170		&module.overridableProperties,
171		&props,
172		module.sdkComponentPropertiesForChildLibrary(),
173	}
174	mctx.CreateModule(LibraryFactory, properties...)
175}
176
177// getApiSurfaceForScope returns the api surface name to use for the apiScope. If one is specified
178// in the corresponding ApiScopeProperties.Api_surface property that is used, otherwise the name of
179// the apiScope is used.
180func (module *SdkLibrary) getApiSurfaceForScope(apiScope *apiScope) *string {
181	scopeProperties := module.scopeToProperties[apiScope]
182
183	apiSurface := scopeProperties.Api_surface
184	if apiSurface == nil {
185		apiSurface = &apiScope.name
186	}
187
188	return apiSurface
189}
190
191// Creates the [Droidstubs] module with ".stubs.source.<[apiScope.name]>" that creates stubs
192// source files from the given full source files and also updates and checks the API
193// specification files (i.e. "*-current.txt", "*-removed.txt" files).
194func (module *SdkLibrary) createDroidstubs(mctx android.DefaultableHookContext, apiScope *apiScope, name string, scopeSpecificDroidstubsArgs []string) {
195	props := struct {
196		Name                             *string
197		Enabled                          proptools.Configurable[bool]
198		Visibility                       []string
199		Srcs                             []string
200		Installable                      *bool
201		Sdk_version                      *string
202		Api_surface                      *string
203		System_modules                   *string
204		Libs                             proptools.Configurable[[]string]
205		Output_javadoc_comments          *bool
206		Arg_files                        []string
207		Args                             *string
208		Java_version                     *string
209		Annotations_enabled              *bool
210		Merge_annotations_dirs           []string
211		Merge_inclusion_annotations_dirs []string
212		Generate_stubs                   *bool
213		Previous_api                     *string
214		Aconfig_declarations             []string
215		Check_api                        struct {
216			Current       ApiToCheck
217			Last_released ApiToCheck
218
219			Api_lint struct {
220				Enabled       *bool
221				New_since     *string
222				Baseline_file *string
223			}
224		}
225		Aidl struct {
226			Include_dirs       []string
227			Local_include_dirs []string
228		}
229		Dists []android.Dist
230	}{}
231
232	// The stubs source processing uses the same compile time classpath when extracting the
233	// API from the implementation library as it does when compiling it. i.e. the same
234	// * sdk version
235	// * system_modules
236	// * libs (static_libs/libs)
237
238	props.Name = proptools.StringPtr(name)
239	props.Enabled = module.EnabledProperty()
240	props.Visibility = childModuleVisibility(module.sdkLibraryProperties.Stubs_source_visibility)
241	props.Srcs = append(props.Srcs, module.properties.Srcs...)
242	props.Srcs = append(props.Srcs, module.sdkLibraryProperties.Api_srcs...)
243	props.Sdk_version = module.deviceProperties.Sdk_version
244	props.Api_surface = module.getApiSurfaceForScope(apiScope)
245	props.System_modules = module.deviceProperties.System_modules
246	props.Installable = proptools.BoolPtr(false)
247	// A droiddoc module has only one Libs property and doesn't distinguish between
248	// shared libs and static libs. So we need to add both of these libs to Libs property.
249	props.Libs = proptools.NewConfigurable[[]string](nil, nil)
250	props.Libs.AppendSimpleValue(module.properties.Libs)
251	props.Libs.Append(module.properties.Static_libs)
252	props.Libs.AppendSimpleValue(module.sdkLibraryProperties.Stub_only_libs)
253	props.Libs.AppendSimpleValue(module.scopeToProperties[apiScope].Libs)
254	props.Aidl.Include_dirs = module.deviceProperties.Aidl.Include_dirs
255	props.Aidl.Local_include_dirs = module.deviceProperties.Aidl.Local_include_dirs
256	props.Java_version = module.properties.Java_version
257
258	props.Annotations_enabled = module.sdkLibraryProperties.Annotations_enabled
259	props.Merge_annotations_dirs = module.sdkLibraryProperties.Merge_annotations_dirs
260	props.Merge_inclusion_annotations_dirs = module.sdkLibraryProperties.Merge_inclusion_annotations_dirs
261	props.Aconfig_declarations = module.sdkLibraryProperties.Aconfig_declarations
262
263	droidstubsArgs := []string{}
264	if len(module.sdkLibraryProperties.Api_packages) != 0 {
265		droidstubsArgs = append(droidstubsArgs, "--stub-packages "+strings.Join(module.sdkLibraryProperties.Api_packages, ":"))
266	}
267	droidstubsArgs = append(droidstubsArgs, module.sdkLibraryProperties.Droiddoc_options...)
268	disabledWarnings := []string{"HiddenSuperclass"}
269	if proptools.BoolDefault(module.sdkLibraryProperties.Api_lint.Legacy_errors_allowed, true) {
270		disabledWarnings = append(disabledWarnings,
271			"BroadcastBehavior",
272			"DeprecationMismatch",
273			"MissingPermission",
274			"SdkConstant",
275			"Todo",
276		)
277	}
278	droidstubsArgs = append(droidstubsArgs, android.JoinWithPrefix(disabledWarnings, "--hide "))
279
280	// Output Javadoc comments for public scope.
281	if apiScope == apiScopePublic {
282		props.Output_javadoc_comments = proptools.BoolPtr(true)
283	}
284
285	// Add in scope specific arguments.
286	droidstubsArgs = append(droidstubsArgs, scopeSpecificDroidstubsArgs...)
287	props.Arg_files = module.sdkLibraryProperties.Droiddoc_option_files
288	props.Args = proptools.StringPtr(strings.Join(droidstubsArgs, " "))
289
290	// List of APIs identified from the provided source files are created. They are later
291	// compared against to the not-yet-released (a.k.a current) list of APIs and to the
292	// last-released (a.k.a numbered) list of API.
293	currentApiFileName := apiScope.apiFilePrefix + "current.txt"
294	removedApiFileName := apiScope.apiFilePrefix + "removed.txt"
295	apiDir := module.getApiDir()
296	currentApiFileName = path.Join(apiDir, currentApiFileName)
297	removedApiFileName = path.Join(apiDir, removedApiFileName)
298
299	// check against the not-yet-release API
300	props.Check_api.Current.Api_file = proptools.StringPtr(currentApiFileName)
301	props.Check_api.Current.Removed_api_file = proptools.StringPtr(removedApiFileName)
302
303	if module.compareAgainstLatestApi(apiScope) {
304		// check against the latest released API
305		latestApiFilegroupName := proptools.StringPtr(module.latestApiFilegroupName(apiScope))
306		props.Previous_api = latestApiFilegroupName
307		props.Check_api.Last_released.Api_file = latestApiFilegroupName
308		props.Check_api.Last_released.Removed_api_file = proptools.StringPtr(
309			module.latestRemovedApiFilegroupName(apiScope))
310		props.Check_api.Last_released.Baseline_file = proptools.StringPtr(
311			module.latestIncompatibilitiesFilegroupName(apiScope))
312
313		if proptools.Bool(module.sdkLibraryProperties.Api_lint.Enabled) {
314			// Enable api lint.
315			props.Check_api.Api_lint.Enabled = proptools.BoolPtr(true)
316			props.Check_api.Api_lint.New_since = latestApiFilegroupName
317
318			// If it exists then pass a lint-baseline.txt through to droidstubs.
319			baselinePath := path.Join(apiDir, apiScope.apiFilePrefix+"lint-baseline.txt")
320			baselinePathRelativeToRoot := path.Join(mctx.ModuleDir(), baselinePath)
321			paths, err := mctx.GlobWithDeps(baselinePathRelativeToRoot, nil)
322			if err != nil {
323				mctx.ModuleErrorf("error checking for presence of %s: %s", baselinePathRelativeToRoot, err)
324			}
325			if len(paths) == 1 {
326				props.Check_api.Api_lint.Baseline_file = proptools.StringPtr(baselinePath)
327			} else if len(paths) != 0 {
328				mctx.ModuleErrorf("error checking for presence of %s: expected one path, found: %v", baselinePathRelativeToRoot, paths)
329			}
330		}
331	}
332
333	if !Bool(module.sdkLibraryProperties.No_dist) {
334		// Dist the api txt and removed api txt artifacts for sdk builds.
335		distDir := proptools.StringPtr(path.Join(module.apiDistPath(apiScope), "api"))
336		stubsTypeTagPrefix := ""
337		if mctx.Config().ReleaseHiddenApiExportableStubs() {
338			stubsTypeTagPrefix = ".exportable"
339		}
340		for _, p := range []struct {
341			tag     string
342			pattern string
343		}{
344			// "exportable" api files are copied to the dist directory instead of the
345			// "everything" api files when "RELEASE_HIDDEN_API_EXPORTABLE_STUBS" build flag
346			// is set. Otherwise, the "everything" api files are copied to the dist directory.
347			{tag: "%s.api.txt", pattern: "%s.txt"},
348			{tag: "%s.removed-api.txt", pattern: "%s-removed.txt"},
349		} {
350			props.Dists = append(props.Dists, android.Dist{
351				Targets: []string{"sdk", "win_sdk"},
352				Dir:     distDir,
353				Dest:    proptools.StringPtr(fmt.Sprintf(p.pattern, module.distStem())),
354				Tag:     proptools.StringPtr(fmt.Sprintf(p.tag, stubsTypeTagPrefix)),
355			})
356		}
357	}
358
359	mctx.CreateModule(DroidstubsFactory, &props, module.sdkComponentPropertiesForChildLibrary()).(*Droidstubs).CallHookIfAvailable(mctx)
360}
361
362type libraryProperties struct {
363	Name           *string
364	Enabled        proptools.Configurable[bool]
365	Visibility     []string
366	Srcs           []string
367	Installable    *bool
368	Sdk_version    *string
369	System_modules *string
370	Patch_module   *string
371	Libs           []string
372	Static_libs    []string
373	Compile_dex    *bool
374	Java_version   *string
375	Openjdk9       struct {
376		Srcs       []string
377		Javacflags []string
378	}
379	Dist struct {
380		Targets []string
381		Dest    *string
382		Dir     *string
383		Tag     *string
384	}
385	Is_stubs_module       *bool
386	Stub_contributing_api *string
387}
388
389func (module *SdkLibrary) stubsLibraryProps(mctx android.DefaultableHookContext, apiScope *apiScope) libraryProperties {
390	props := libraryProperties{}
391	props.Enabled = module.EnabledProperty()
392	props.Visibility = []string{"//visibility:override", "//visibility:private"}
393	// sources are generated from the droiddoc
394	sdkVersion := module.sdkVersionForStubsLibrary(mctx, apiScope)
395	props.Sdk_version = proptools.StringPtr(sdkVersion)
396	props.System_modules = module.deviceProperties.System_modules
397	props.Patch_module = module.properties.Patch_module
398	props.Installable = proptools.BoolPtr(false)
399	props.Libs = module.sdkLibraryProperties.Stub_only_libs
400	props.Libs = append(props.Libs, module.scopeToProperties[apiScope].Libs...)
401	props.Static_libs = module.sdkLibraryProperties.Stub_only_static_libs
402	// The stub-annotations library contains special versions of the annotations
403	// with CLASS retention policy, so that they're kept.
404	if proptools.Bool(module.sdkLibraryProperties.Annotations_enabled) {
405		props.Libs = append(props.Libs, "stub-annotations")
406	}
407	props.Openjdk9.Srcs = module.properties.Openjdk9.Srcs
408	props.Openjdk9.Javacflags = module.properties.Openjdk9.Javacflags
409	// We compile the stubs for 1.8 in line with the main android.jar stubs, and potential
410	// interop with older developer tools that don't support 1.9.
411	props.Java_version = proptools.StringPtr("1.8")
412	props.Is_stubs_module = proptools.BoolPtr(true)
413	props.Stub_contributing_api = proptools.StringPtr(apiScope.kind.String())
414
415	return props
416}
417
418// Creates the from-source stub [Library] with ".stubs.<[apiScope.name]>.from-source" suffix.
419func (module *SdkLibrary) createFromSourceStubsLibrary(mctx android.DefaultableHookContext, apiScope *apiScope) {
420
421	props := module.stubsLibraryProps(mctx, apiScope)
422	props.Name = proptools.StringPtr(module.fromSourceStubsLibraryModuleName(apiScope))
423	props.Srcs = []string{":" + module.droidstubsModuleName(apiScope)}
424
425	mctx.CreateModule(LibraryFactory, &props, module.sdkComponentPropertiesForChildLibrary())
426}
427
428// Creates the "exportable" from-source stub [Library] with
429// ".stubs.exportable.<[apiScope.name]>" suffix.
430func (module *SdkLibrary) createExportableFromSourceStubsLibrary(mctx android.DefaultableHookContext, apiScope *apiScope) {
431	props := module.stubsLibraryProps(mctx, apiScope)
432	props.Name = proptools.StringPtr(module.exportableFromSourceStubsLibraryModuleName(apiScope))
433	props.Srcs = []string{":" + module.droidstubsModuleName(apiScope) + "{.exportable}"}
434
435	mctx.CreateModule(LibraryFactory, &props, module.sdkComponentPropertiesForChildLibrary())
436}
437
438// Creates the from-text stub [ApiLibrary] with ".stubs.<[apiScope.name]>.from-text" suffix.
439func (module *SdkLibrary) createApiLibrary(mctx android.DefaultableHookContext, apiScope *apiScope) {
440	props := struct {
441		Name              *string
442		Enabled           proptools.Configurable[bool]
443		Visibility        []string
444		Api_contributions []string
445		Libs              proptools.Configurable[[]string]
446		Static_libs       []string
447		System_modules    *string
448		Enable_validation *bool
449		Stubs_type        *string
450		Sdk_version       *string
451		Previous_api      *string
452	}{}
453
454	props.Name = proptools.StringPtr(module.fromTextStubsLibraryModuleName(apiScope))
455	props.Enabled = module.EnabledProperty()
456	props.Visibility = []string{"//visibility:override", "//visibility:private"}
457
458	apiContributions := []string{}
459
460	// Api surfaces are not independent of each other, but have subset relationships,
461	// and so does the api files. To generate from-text stubs for api surfaces other than public,
462	// all subset api domains' api_contriubtions must be added as well.
463	scope := apiScope
464	for scope != nil {
465		apiContributions = append(apiContributions, module.droidstubsModuleName(scope)+".api.contribution")
466		scope = scope.extends
467	}
468	if apiScope == apiScopePublic {
469		additionalApiContribution := module.apiLibraryAdditionalApiContribution()
470		if additionalApiContribution != "" {
471			apiContributions = append(apiContributions, additionalApiContribution)
472		}
473	}
474
475	props.Api_contributions = apiContributions
476
477	// Ensure that stub-annotations is added to the classpath before any other libs
478	props.Libs = proptools.NewConfigurable[[]string](nil, nil)
479	props.Libs.AppendSimpleValue([]string{"stub-annotations"})
480	props.Libs.AppendSimpleValue(module.properties.Libs)
481	props.Libs.Append(module.properties.Static_libs)
482	props.Libs.AppendSimpleValue(module.sdkLibraryProperties.Stub_only_libs)
483	props.Libs.AppendSimpleValue(module.scopeToProperties[apiScope].Libs)
484	props.Static_libs = module.sdkLibraryProperties.Stub_only_static_libs
485
486	props.System_modules = module.deviceProperties.System_modules
487	props.Enable_validation = proptools.BoolPtr(true)
488	props.Stubs_type = proptools.StringPtr("everything")
489
490	if module.deviceProperties.Sdk_version != nil {
491		props.Sdk_version = module.deviceProperties.Sdk_version
492	}
493
494	if module.compareAgainstLatestApi(apiScope) {
495		// check against the latest released API
496		latestApiFilegroupName := proptools.StringPtr(module.latestApiFilegroupName(apiScope))
497		props.Previous_api = latestApiFilegroupName
498	}
499
500	mctx.CreateModule(ApiLibraryFactory, &props, module.sdkComponentPropertiesForChildLibrary())
501}
502
503func (module *SdkLibrary) topLevelStubsLibraryProps(mctx android.DefaultableHookContext, apiScope *apiScope, doDist bool) libraryProperties {
504	props := libraryProperties{}
505
506	props.Enabled = module.EnabledProperty()
507	props.Visibility = childModuleVisibility(module.sdkLibraryProperties.Stubs_library_visibility)
508	sdkVersion := module.sdkVersionForStubsLibrary(mctx, apiScope)
509	props.Sdk_version = proptools.StringPtr(sdkVersion)
510
511	props.System_modules = module.deviceProperties.System_modules
512
513	// The imports need to be compiled to dex if the java_sdk_library requests it.
514	compileDex := module.dexProperties.Compile_dex
515	if module.stubLibrariesCompiledForDex() {
516		compileDex = proptools.BoolPtr(true)
517	}
518	props.Compile_dex = compileDex
519
520	props.Stub_contributing_api = proptools.StringPtr(apiScope.kind.String())
521
522	if !Bool(module.sdkLibraryProperties.No_dist) && doDist {
523		props.Dist.Targets = []string{"sdk", "win_sdk"}
524		props.Dist.Dest = proptools.StringPtr(fmt.Sprintf("%v.jar", module.distStem()))
525		props.Dist.Dir = proptools.StringPtr(module.apiDistPath(apiScope))
526		props.Dist.Tag = proptools.StringPtr(".jar")
527	}
528	props.Is_stubs_module = proptools.BoolPtr(true)
529
530	return props
531}
532
533// Creates the stub [Library] with ".stubs.<[apiScope.name]>" suffix.
534func (module *SdkLibrary) createTopLevelStubsLibrary(
535	mctx android.DefaultableHookContext, apiScope *apiScope) {
536
537	// Dist the "everything" stubs when the RELEASE_HIDDEN_API_EXPORTABLE_STUBS build flag is false
538	doDist := !mctx.Config().ReleaseHiddenApiExportableStubs()
539	props := module.topLevelStubsLibraryProps(mctx, apiScope, doDist)
540	props.Name = proptools.StringPtr(module.stubsLibraryModuleName(apiScope))
541
542	// Add the stub compiling java_library/java_api_library as static lib based on build config
543	staticLib := module.fromSourceStubsLibraryModuleName(apiScope)
544	if mctx.Config().BuildFromTextStub() && module.ModuleBuildFromTextStubs() {
545		staticLib = module.fromTextStubsLibraryModuleName(apiScope)
546	}
547	props.Static_libs = append(props.Static_libs, staticLib)
548
549	mctx.CreateModule(LibraryFactory, &props, module.sdkComponentPropertiesForChildLibrary())
550}
551
552// Creates the "exportable" stub [Library] with ".stubs.exportable.<[apiScope.name]>" suffix.
553func (module *SdkLibrary) createTopLevelExportableStubsLibrary(
554	mctx android.DefaultableHookContext, apiScope *apiScope) {
555
556	// Dist the "exportable" stubs when the RELEASE_HIDDEN_API_EXPORTABLE_STUBS build flag is true
557	doDist := mctx.Config().ReleaseHiddenApiExportableStubs()
558	props := module.topLevelStubsLibraryProps(mctx, apiScope, doDist)
559	props.Name = proptools.StringPtr(module.exportableStubsLibraryModuleName(apiScope))
560
561	staticLib := module.exportableFromSourceStubsLibraryModuleName(apiScope)
562	props.Static_libs = append(props.Static_libs, staticLib)
563
564	mctx.CreateModule(LibraryFactory, &props, module.sdkComponentPropertiesForChildLibrary())
565}
566
567// Creates the [sdkLibraryXml] with ".xml" suffix.
568func (module *SdkLibrary) createXmlFile(mctx android.DefaultableHookContext) {
569	moduleMinApiLevel := module.Library.MinSdkVersion(mctx)
570	var moduleMinApiLevelStr = moduleMinApiLevel.String()
571	if moduleMinApiLevel == android.NoneApiLevel {
572		moduleMinApiLevelStr = "current"
573	}
574	props := struct {
575		Name                      *string
576		Enabled                   proptools.Configurable[bool]
577		Lib_name                  *string
578		Apex_available            []string
579		On_bootclasspath_since    *string
580		On_bootclasspath_before   *string
581		Min_device_sdk            *string
582		Max_device_sdk            *string
583		Sdk_library_min_api_level *string
584		Uses_libs_dependencies    proptools.Configurable[[]string]
585	}{
586		Name:                      proptools.StringPtr(module.xmlPermissionsModuleName()),
587		Enabled:                   module.EnabledProperty(),
588		Lib_name:                  proptools.StringPtr(module.BaseModuleName()),
589		Apex_available:            module.ApexProperties.Apex_available,
590		On_bootclasspath_since:    module.commonSdkLibraryProperties.On_bootclasspath_since,
591		On_bootclasspath_before:   module.commonSdkLibraryProperties.On_bootclasspath_before,
592		Min_device_sdk:            module.commonSdkLibraryProperties.Min_device_sdk,
593		Max_device_sdk:            module.commonSdkLibraryProperties.Max_device_sdk,
594		Sdk_library_min_api_level: &moduleMinApiLevelStr,
595		Uses_libs_dependencies:    module.usesLibraryProperties.Uses_libs.Clone(),
596	}
597
598	mctx.CreateModule(sdkLibraryXmlFactory, &props)
599}
600
601// ---------------------------------------------------------------------------------------------
602// Build rules of the submodules generated by java_sdk_library_import.
603// Note that the java_sdk_library_import module does not generate the implementation library.
604// Instead, it will create a dependency to the source implemenetation library if one exists.
605// java_sdk_library_import "framework-foo" generates the following submodules:
606//
607// - "framework-foo.stubs.<[apiScope.name]>" (type: [Import]): prebuilt stub library module that
608//		provides the stub jar file checked in the tree.
609//
610// - "framework-foo.stubs.source.<[apiScope.name]>" (type: [PrebuiltStubsSources]): prebuilt
611//		droidstubs module that provides the stub source jar file checked in the tree.
612//
613// - "framework-foo.stubs.source.<[apiScope.name]>.api.contribution"
614//		(type [JavaApiContributionImport]): prebuilt java_api_contribution module that provides
615//		the prebuilt api file for previously released from-text stub generation.
616// ---------------------------------------------------------------------------------------------
617
618// Creates the prebuilt stub [Import] with ".stubs.<[apiScope.name]>" suffix.
619func (module *SdkLibraryImport) createJavaImportForStubs(mctx android.DefaultableHookContext, apiScope *apiScope, scopeProperties *sdkLibraryScopeProperties) {
620	// Creates a java import for the jar with ".stubs" suffix
621	props := struct {
622		Name                             *string
623		Source_module_name               *string
624		Created_by_java_sdk_library_name *string
625		Sdk_version                      *string
626		Libs                             []string
627		Jars                             []string
628		Compile_dex                      *bool
629		Is_stubs_module                  *bool
630
631		android.UserSuppliedPrebuiltProperties
632	}{}
633	props.Name = proptools.StringPtr(module.stubsLibraryModuleName(apiScope))
634	props.Source_module_name = proptools.StringPtr(apiScope.stubsLibraryModuleName(module.BaseModuleName()))
635	props.Created_by_java_sdk_library_name = proptools.StringPtr(module.RootLibraryName())
636	props.Sdk_version = scopeProperties.Sdk_version
637	// Prepend any of the libs from the legacy public properties to the libs for each of the
638	// scopes to avoid having to duplicate them in each scope.
639	props.Libs = append(module.properties.Libs, scopeProperties.Libs...)
640	props.Jars = scopeProperties.Jars
641
642	// The imports are preferred if the java_sdk_library_import is preferred.
643	props.CopyUserSuppliedPropertiesFromPrebuilt(&module.prebuilt)
644
645	// The imports need to be compiled to dex if the java_sdk_library_import requests it.
646	compileDex := module.properties.Compile_dex
647	if module.stubLibrariesCompiledForDex() {
648		compileDex = proptools.BoolPtr(true)
649	}
650	props.Compile_dex = compileDex
651	props.Is_stubs_module = proptools.BoolPtr(true)
652
653	mctx.CreateModule(ImportFactory, &props, module.sdkComponentPropertiesForChildLibrary())
654}
655
656func (module *SdkLibraryImport) createPrebuiltStubsSources(mctx android.DefaultableHookContext, apiScope *apiScope, scopeProperties *sdkLibraryScopeProperties) {
657	props := struct {
658		Name                             *string
659		Source_module_name               *string
660		Created_by_java_sdk_library_name *string
661		Srcs                             []string
662
663		android.UserSuppliedPrebuiltProperties
664	}{}
665	props.Name = proptools.StringPtr(module.droidstubsModuleName(apiScope))
666	props.Source_module_name = proptools.StringPtr(apiScope.stubsSourceModuleName(module.BaseModuleName()))
667	props.Created_by_java_sdk_library_name = proptools.StringPtr(module.RootLibraryName())
668	props.Srcs = scopeProperties.Stub_srcs
669
670	// The stubs source is preferred if the java_sdk_library_import is preferred.
671	props.CopyUserSuppliedPropertiesFromPrebuilt(&module.prebuilt)
672
673	mctx.CreateModule(PrebuiltStubsSourcesFactory, &props, module.sdkComponentPropertiesForChildLibrary())
674}
675
676// Creates the prebuilt api contribution [JavaApiContributionImport] with
677// ".stubs.source.<[apiScope.name]>.api.contribution" suffix.
678func (module *SdkLibraryImport) createPrebuiltApiContribution(mctx android.DefaultableHookContext, apiScope *apiScope, scopeProperties *sdkLibraryScopeProperties) {
679	api_file := scopeProperties.Current_api
680	api_surface := &apiScope.name
681
682	props := struct {
683		Name                             *string
684		Source_module_name               *string
685		Created_by_java_sdk_library_name *string
686		Api_surface                      *string
687		Api_file                         *string
688		Visibility                       []string
689	}{}
690
691	props.Name = proptools.StringPtr(module.droidstubsModuleName(apiScope) + ".api.contribution")
692	props.Source_module_name = proptools.StringPtr(apiScope.stubsSourceModuleName(module.BaseModuleName()) + ".api.contribution")
693	props.Created_by_java_sdk_library_name = proptools.StringPtr(module.RootLibraryName())
694	props.Api_surface = api_surface
695	props.Api_file = api_file
696	props.Visibility = []string{"//visibility:override", "//visibility:public"}
697
698	mctx.CreateModule(ApiContributionImportFactory, &props, module.sdkComponentPropertiesForChildLibrary())
699}
700
701// ---------------------------------------------------------------------------------------------
702// End of the build rules of the submodules generated by java_sdk_library_import.
703// ---------------------------------------------------------------------------------------------
704
705// Definition of the [sdkLibraryXml] module. The module generates the permissions xml file,
706// so that the apps can specify the java_sdk_library using <uses-permission> tag in the
707// AndroidManifest.xml file.
708type sdkLibraryXml struct {
709	android.ModuleBase
710	android.DefaultableModuleBase
711	android.ApexModuleBase
712
713	properties sdkLibraryXmlProperties
714
715	outputFilePath android.OutputPath
716	installDirPath android.InstallPath
717
718	hideApexVariantFromMake bool
719}
720
721type sdkLibraryXmlProperties struct {
722	// canonical name of the lib
723	Lib_name *string
724
725	// Signals that this shared library is part of the bootclasspath starting
726	// on the version indicated in this attribute.
727	//
728	// This will make platforms at this level and above to ignore
729	// <uses-library> tags with this library name because the library is already
730	// available
731	On_bootclasspath_since *string
732
733	// Signals that this shared library was part of the bootclasspath before
734	// (but not including) the version indicated in this attribute.
735	//
736	// The system will automatically add a <uses-library> tag with this library to
737	// apps that target any SDK less than the version indicated in this attribute.
738	On_bootclasspath_before *string
739
740	// Indicates that PackageManager should ignore this shared library if the
741	// platform is below the version indicated in this attribute.
742	//
743	// This means that the device won't recognise this library as installed.
744	Min_device_sdk *string
745
746	// Indicates that PackageManager should ignore this shared library if the
747	// platform is above the version indicated in this attribute.
748	//
749	// This means that the device won't recognise this library as installed.
750	Max_device_sdk *string
751
752	// The SdkLibrary's min api level as a string
753	//
754	// This value comes from the ApiLevel of the MinSdkVersion property.
755	Sdk_library_min_api_level *string
756
757	// Uses-libs dependencies that the shared library requires to work correctly.
758	//
759	// This will add dependency="foo:bar" to the <library> section.
760	Uses_libs_dependencies proptools.Configurable[[]string]
761}
762
763// java_sdk_library_xml builds the permission xml file for a java_sdk_library.
764// Not to be used directly by users. java_sdk_library internally uses this.
765func sdkLibraryXmlFactory() android.Module {
766	module := &sdkLibraryXml{}
767
768	module.AddProperties(&module.properties)
769
770	android.InitApexModule(module)
771	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
772
773	return module
774}
775
776func (module *sdkLibraryXml) UniqueApexVariations() bool {
777	// sdkLibraryXml needs a unique variation per APEX because the generated XML file contains the path to the
778	// mounted APEX, which contains the name of the APEX.
779	return true
780}
781
782// from android.PrebuiltEtcModule
783func (module *sdkLibraryXml) BaseDir() string {
784	return "etc"
785}
786
787// from android.PrebuiltEtcModule
788func (module *sdkLibraryXml) SubDir() string {
789	return "permissions"
790}
791
792var _ etc.PrebuiltEtcModule = (*sdkLibraryXml)(nil)
793
794// from android.ApexModule
795func (module *sdkLibraryXml) AvailableFor(what string) bool {
796	return android.CheckAvailableForApex(what, module.ApexAvailableFor())
797}
798
799func (module *sdkLibraryXml) ApexAvailableFor() []string {
800	return []string{android.AvailableToPlatform, android.AvailableToAnyApex}
801}
802
803func (module *sdkLibraryXml) DepsMutator(ctx android.BottomUpMutatorContext) {
804	// do nothing
805}
806
807var _ android.ApexModule = (*sdkLibraryXml)(nil)
808
809// Implements android.ApexModule
810func (m *sdkLibraryXml) MinSdkVersionSupported(ctx android.BaseModuleContext) android.ApiLevel {
811	return android.MinApiLevel
812}
813
814// File path to the runtime implementation library
815func (module *sdkLibraryXml) implPath(ctx android.ModuleContext) string {
816	implName := proptools.String(module.properties.Lib_name)
817	if apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider); !apexInfo.IsForPlatform() {
818		// TODO(b/146468504): ApexVariationName() is only a soong module name, not apex name.
819		// In most cases, this works fine. But when apex_name is set or override_apex is used
820		// this can be wrong.
821		return fmt.Sprintf("/apex/%s/javalib/%s.jar", apexInfo.BaseApexName, implName)
822	}
823	partition := "system"
824	if module.SocSpecific() {
825		partition = "vendor"
826	} else if module.DeviceSpecific() {
827		partition = "odm"
828	} else if module.ProductSpecific() {
829		partition = "product"
830	} else if module.SystemExtSpecific() {
831		partition = "system_ext"
832	}
833	return "/" + partition + "/framework/" + implName + ".jar"
834}
835
836func formattedOptionalSdkLevelAttribute(ctx android.ModuleContext, attrName string, value *string) string {
837	if value == nil {
838		return ""
839	}
840	apiLevel, err := android.ApiLevelFromUser(ctx, *value)
841	if err != nil {
842		// attributes in bp files have underscores but in the xml have dashes.
843		ctx.PropertyErrorf(strings.ReplaceAll(attrName, "-", "_"), err.Error())
844		return ""
845	}
846	if apiLevel.IsCurrent() {
847		// passing "current" would always mean a future release, never the current (or the current in
848		// progress) which means some conditions would never be triggered.
849		ctx.PropertyErrorf(strings.ReplaceAll(attrName, "-", "_"),
850			`"current" is not an allowed value for this attribute`)
851		return ""
852	}
853	// "safeValue" is safe because it translates finalized codenames to a string
854	// with their SDK int.
855	safeValue := apiLevel.String()
856	return formattedOptionalAttribute(attrName, &safeValue)
857}
858
859// formats an attribute for the xml permissions file if the value is not null
860// returns empty string otherwise
861func formattedOptionalAttribute(attrName string, value *string) string {
862	if value == nil {
863		return ""
864	}
865	return fmt.Sprintf("        %s=\"%s\"\n", attrName, *value)
866}
867
868func formattedDependenciesAttribute(dependencies []string) string {
869	if dependencies == nil {
870		return ""
871	}
872	return fmt.Sprintf("        dependency=\"%s\"\n", strings.Join(dependencies, ":"))
873}
874
875func (module *sdkLibraryXml) permissionsContents(ctx android.ModuleContext) string {
876	libName := proptools.String(module.properties.Lib_name)
877	libNameAttr := formattedOptionalAttribute("name", &libName)
878	filePath := module.implPath(ctx)
879	filePathAttr := formattedOptionalAttribute("file", &filePath)
880	implicitFromAttr := formattedOptionalSdkLevelAttribute(ctx, "on-bootclasspath-since", module.properties.On_bootclasspath_since)
881	implicitUntilAttr := formattedOptionalSdkLevelAttribute(ctx, "on-bootclasspath-before", module.properties.On_bootclasspath_before)
882	minSdkAttr := formattedOptionalSdkLevelAttribute(ctx, "min-device-sdk", module.properties.Min_device_sdk)
883	maxSdkAttr := formattedOptionalSdkLevelAttribute(ctx, "max-device-sdk", module.properties.Max_device_sdk)
884	dependenciesAttr := formattedDependenciesAttribute(module.properties.Uses_libs_dependencies.GetOrDefault(ctx, nil))
885	// <library> is understood in all android versions whereas <apex-library> is only understood from API T (and ignored before that).
886	// similarly, min_device_sdk is only understood from T. So if a library is using that, we need to use the apex-library to make sure this library is not loaded before T
887	var libraryTag string
888	if module.properties.Min_device_sdk != nil {
889		libraryTag = "    <apex-library\n"
890	} else {
891		libraryTag = "    <library\n"
892	}
893
894	return strings.Join([]string{
895		"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n",
896		"<!-- Copyright (C) 2018 The Android Open Source Project\n",
897		"\n",
898		"    Licensed under the Apache License, Version 2.0 (the \"License\");\n",
899		"    you may not use this file except in compliance with the License.\n",
900		"    You may obtain a copy of the License at\n",
901		"\n",
902		"        http://www.apache.org/licenses/LICENSE-2.0\n",
903		"\n",
904		"    Unless required by applicable law or agreed to in writing, software\n",
905		"    distributed under the License is distributed on an \"AS IS\" BASIS,\n",
906		"    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
907		"    See the License for the specific language governing permissions and\n",
908		"    limitations under the License.\n",
909		"-->\n",
910		"<permissions>\n",
911		libraryTag,
912		libNameAttr,
913		filePathAttr,
914		implicitFromAttr,
915		implicitUntilAttr,
916		minSdkAttr,
917		maxSdkAttr,
918		dependenciesAttr,
919		"    />\n",
920		"</permissions>\n",
921	}, "")
922}
923
924func (module *sdkLibraryXml) GenerateAndroidBuildActions(ctx android.ModuleContext) {
925	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
926	module.hideApexVariantFromMake = !apexInfo.IsForPlatform()
927
928	libName := proptools.String(module.properties.Lib_name)
929	module.selfValidate(ctx)
930	xmlContent := module.permissionsContents(ctx)
931
932	module.outputFilePath = android.PathForModuleOut(ctx, libName+".xml").OutputPath
933	android.WriteFileRuleVerbatim(ctx, module.outputFilePath, xmlContent)
934
935	module.installDirPath = android.PathForModuleInstall(ctx, "etc", module.SubDir())
936	ctx.PackageFile(module.installDirPath, libName+".xml", module.outputFilePath)
937
938	ctx.SetOutputFiles(android.OutputPaths{module.outputFilePath}.Paths(), "")
939
940	etc.SetCommonPrebuiltEtcInfo(ctx, module)
941}
942
943func (module *sdkLibraryXml) AndroidMkEntries() []android.AndroidMkEntries {
944	if module.hideApexVariantFromMake {
945		return []android.AndroidMkEntries{{
946			Disabled: true,
947		}}
948	}
949
950	return []android.AndroidMkEntries{{
951		Class:      "ETC",
952		OutputFile: android.OptionalPathForPath(module.outputFilePath),
953		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
954			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
955				entries.SetString("LOCAL_MODULE_TAGS", "optional")
956				entries.SetString("LOCAL_MODULE_PATH", module.installDirPath.String())
957				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", module.outputFilePath.Base())
958			},
959		},
960	}}
961}
962
963func (module *sdkLibraryXml) selfValidate(ctx android.ModuleContext) {
964	module.validateAtLeastTAttributes(ctx)
965	module.validateMinAndMaxDeviceSdk(ctx)
966	module.validateMinMaxDeviceSdkAndModuleMinSdk(ctx)
967	module.validateOnBootclasspathBeforeRequirements(ctx)
968}
969
970func (module *sdkLibraryXml) validateAtLeastTAttributes(ctx android.ModuleContext) {
971	t := android.ApiLevelOrPanic(ctx, "Tiramisu")
972	module.attrAtLeastT(ctx, t, module.properties.Min_device_sdk, "min_device_sdk")
973	module.attrAtLeastT(ctx, t, module.properties.Max_device_sdk, "max_device_sdk")
974	module.attrAtLeastT(ctx, t, module.properties.On_bootclasspath_before, "on_bootclasspath_before")
975	module.attrAtLeastT(ctx, t, module.properties.On_bootclasspath_since, "on_bootclasspath_since")
976}
977
978func (module *sdkLibraryXml) attrAtLeastT(ctx android.ModuleContext, t android.ApiLevel, attr *string, attrName string) {
979	if attr != nil {
980		if level, err := android.ApiLevelFromUser(ctx, *attr); err == nil {
981			// we will inform the user of invalid inputs when we try to write the
982			// permissions xml file so we don't need to do it here
983			if t.GreaterThan(level) {
984				ctx.PropertyErrorf(attrName, "Attribute value needs to be at least T")
985			}
986		}
987	}
988}
989
990func (module *sdkLibraryXml) validateMinAndMaxDeviceSdk(ctx android.ModuleContext) {
991	if module.properties.Min_device_sdk != nil && module.properties.Max_device_sdk != nil {
992		min, minErr := android.ApiLevelFromUser(ctx, *module.properties.Min_device_sdk)
993		max, maxErr := android.ApiLevelFromUser(ctx, *module.properties.Max_device_sdk)
994		if minErr == nil && maxErr == nil {
995			// we will inform the user of invalid inputs when we try to write the
996			// permissions xml file so we don't need to do it here
997			if min.GreaterThan(max) {
998				ctx.ModuleErrorf("min_device_sdk can't be greater than max_device_sdk")
999			}
1000		}
1001	}
1002}
1003
1004func (module *sdkLibraryXml) validateMinMaxDeviceSdkAndModuleMinSdk(ctx android.ModuleContext) {
1005	moduleMinApi := android.ApiLevelOrPanic(ctx, *module.properties.Sdk_library_min_api_level)
1006	if module.properties.Min_device_sdk != nil {
1007		api, err := android.ApiLevelFromUser(ctx, *module.properties.Min_device_sdk)
1008		if err == nil {
1009			if moduleMinApi.GreaterThan(api) {
1010				ctx.PropertyErrorf("min_device_sdk", "Can't be less than module's min sdk (%s)", moduleMinApi)
1011			}
1012		}
1013	}
1014	if module.properties.Max_device_sdk != nil {
1015		api, err := android.ApiLevelFromUser(ctx, *module.properties.Max_device_sdk)
1016		if err == nil {
1017			if moduleMinApi.GreaterThan(api) {
1018				ctx.PropertyErrorf("max_device_sdk", "Can't be less than module's min sdk (%s)", moduleMinApi)
1019			}
1020		}
1021	}
1022}
1023
1024func (module *sdkLibraryXml) validateOnBootclasspathBeforeRequirements(ctx android.ModuleContext) {
1025	moduleMinApi := android.ApiLevelOrPanic(ctx, *module.properties.Sdk_library_min_api_level)
1026	if module.properties.On_bootclasspath_before != nil {
1027		t := android.ApiLevelOrPanic(ctx, "Tiramisu")
1028		// if we use the attribute, then we need to do this validation
1029		if moduleMinApi.LessThan(t) {
1030			// if minAPi is < T, then we need to have min_device_sdk (which only accepts T+)
1031			if module.properties.Min_device_sdk == nil {
1032				ctx.PropertyErrorf("on_bootclasspath_before", "Using this property requires that the module's min_sdk_version or the shared library's min_device_sdk is at least T")
1033			}
1034		}
1035	}
1036}
1037