1// Copyright (C) 2021 The Android Open Source Project
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	"strings"
20
21	"android/soong/android"
22	"github.com/google/blueprint"
23)
24
25// Contains support for processing hiddenAPI in a modular fashion.
26
27// HiddenAPIScope encapsulates all the information that the hidden API processing needs about API
28// scopes, i.e. what is called android.SdkKind and apiScope. It does not just use those as they do
29// not provide the information needed by hidden API processing.
30type HiddenAPIScope struct {
31	// The name of the scope, used for debug purposes.
32	name string
33
34	// The corresponding android.SdkKind, used for retrieving paths from java_sdk_library* modules.
35	sdkKind android.SdkKind
36
37	// The option needed to passed to "hiddenapi list".
38	hiddenAPIListOption string
39
40	// The name sof the source stub library modules that contain the API provided by the platform,
41	// i.e. by modules that are not in an APEX.
42	nonUpdatableSourceModule string
43
44	// The names of the prebuilt stub library modules that contain the API provided by the platform,
45	// i.e. by modules that are not in an APEX.
46	nonUpdatablePrebuiltModule string
47}
48
49// initHiddenAPIScope initializes the scope.
50func initHiddenAPIScope(apiScope *HiddenAPIScope) *HiddenAPIScope {
51	sdkKind := apiScope.sdkKind
52	// The platform does not provide a core platform API.
53	if sdkKind != android.SdkCorePlatform {
54		kindAsString := sdkKind.String()
55		var insert string
56		if sdkKind == android.SdkPublic {
57			insert = ""
58		} else {
59			insert = "." + strings.ReplaceAll(kindAsString, "-", "_")
60		}
61
62		nonUpdatableModule := "android-non-updatable"
63
64		// Construct the name of the android-non-updatable source module for this scope.
65		apiScope.nonUpdatableSourceModule = fmt.Sprintf("%s.stubs%s", nonUpdatableModule, insert)
66
67		prebuiltModuleName := func(name string, kind string) string {
68			return fmt.Sprintf("sdk_%s_current_%s", kind, name)
69		}
70
71		// Construct the name of the android-non-updatable prebuilt module for this scope.
72		apiScope.nonUpdatablePrebuiltModule = prebuiltModuleName(nonUpdatableModule, kindAsString)
73	}
74
75	return apiScope
76}
77
78// android-non-updatable takes the name of a module and returns a possibly scope specific name of
79// the module.
80func (l *HiddenAPIScope) scopeSpecificStubModule(ctx android.BaseModuleContext, name string) string {
81	// The android-non-updatable is not a java_sdk_library but there are separate stub libraries for
82	// each scope.
83	// TODO(b/192067200): Remove special handling of android-non-updatable.
84	if name == "android-non-updatable" {
85		if ctx.Config().AlwaysUsePrebuiltSdks() {
86			return l.nonUpdatablePrebuiltModule
87		} else {
88			return l.nonUpdatableSourceModule
89		}
90	} else {
91		// Assume that the module is either a java_sdk_library (or equivalent) and so will provide
92		// separate stub jars for each scope or is a java_library (or equivalent) in which case it will
93		// have the same stub jar for each scope.
94		return name
95	}
96}
97
98func (l *HiddenAPIScope) String() string {
99	return fmt.Sprintf("HiddenAPIScope{%s}", l.name)
100}
101
102var (
103	PublicHiddenAPIScope = initHiddenAPIScope(&HiddenAPIScope{
104		name:                "public",
105		sdkKind:             android.SdkPublic,
106		hiddenAPIListOption: "--public-stub-classpath",
107	})
108	SystemHiddenAPIScope = initHiddenAPIScope(&HiddenAPIScope{
109		name:                "system",
110		sdkKind:             android.SdkSystem,
111		hiddenAPIListOption: "--system-stub-classpath",
112	})
113	TestHiddenAPIScope = initHiddenAPIScope(&HiddenAPIScope{
114		name:                "test",
115		sdkKind:             android.SdkTest,
116		hiddenAPIListOption: "--test-stub-classpath",
117	})
118	ModuleLibHiddenAPIScope = initHiddenAPIScope(&HiddenAPIScope{
119		name:    "module-lib",
120		sdkKind: android.SdkModule,
121	})
122	CorePlatformHiddenAPIScope = initHiddenAPIScope(&HiddenAPIScope{
123		name:                "core-platform",
124		sdkKind:             android.SdkCorePlatform,
125		hiddenAPIListOption: "--core-platform-stub-classpath",
126	})
127
128	// hiddenAPIRelevantSdkKinds lists all the android.SdkKind instances that are needed by the hidden
129	// API processing.
130	//
131	// These are roughly in order from narrowest API surface to widest. Widest means the API stubs
132	// with the biggest API surface, e.g. test is wider than system is wider than public.
133	//
134	// Core platform is considered wider than system/module-lib because those modules that provide
135	// core platform APIs either do not have any system/module-lib APIs at all, or if they do it is
136	// because the core platform API is being converted to system/module-lib APIs. In either case the
137	// system/module-lib APIs are subsets of the core platform API.
138	//
139	// This is not strictly in order from narrowest to widest as the Test API is wider than system but
140	// is neither wider or narrower than the module-lib or core platform APIs. However, this works
141	// well enough at the moment.
142	// TODO(b/191644675): Correctly reflect the sub/superset relationships between APIs.
143	hiddenAPIScopes = []*HiddenAPIScope{
144		PublicHiddenAPIScope,
145		SystemHiddenAPIScope,
146		TestHiddenAPIScope,
147		ModuleLibHiddenAPIScope,
148		CorePlatformHiddenAPIScope,
149	}
150
151	// The HiddenAPIScope instances that are supported by a java_sdk_library.
152	//
153	// CorePlatformHiddenAPIScope is not used as the java_sdk_library does not have special support
154	// for core_platform API, instead it is implemented as a customized form of PublicHiddenAPIScope.
155	hiddenAPISdkLibrarySupportedScopes = []*HiddenAPIScope{
156		PublicHiddenAPIScope,
157		SystemHiddenAPIScope,
158		TestHiddenAPIScope,
159		ModuleLibHiddenAPIScope,
160	}
161
162	// The HiddenAPIScope instances that are supported by the `hiddenapi list`.
163	hiddenAPIFlagScopes = []*HiddenAPIScope{
164		PublicHiddenAPIScope,
165		SystemHiddenAPIScope,
166		TestHiddenAPIScope,
167		CorePlatformHiddenAPIScope,
168	}
169)
170
171type hiddenAPIStubsDependencyTag struct {
172	blueprint.BaseDependencyTag
173
174	// The api scope for which this dependency was added.
175	apiScope *HiddenAPIScope
176
177	// Indicates that the dependency is not for an API provided by the current bootclasspath fragment
178	// but is an additional API provided by a module that is not part of the current bootclasspath
179	// fragment.
180	fromAdditionalDependency bool
181}
182
183func (b hiddenAPIStubsDependencyTag) ExcludeFromApexContents() {
184}
185
186func (b hiddenAPIStubsDependencyTag) ReplaceSourceWithPrebuilt() bool {
187	return false
188}
189
190func (b hiddenAPIStubsDependencyTag) SdkMemberType(child android.Module) android.SdkMemberType {
191	// Do not add additional dependencies to the sdk.
192	if b.fromAdditionalDependency {
193		return nil
194	}
195
196	// If the module is a java_sdk_library then treat it as if it was specific in the java_sdk_libs
197	// property, otherwise treat if it was specified in the java_header_libs property.
198	if javaSdkLibrarySdkMemberType.IsInstance(child) {
199		return javaSdkLibrarySdkMemberType
200	}
201
202	return javaHeaderLibsSdkMemberType
203}
204
205func (b hiddenAPIStubsDependencyTag) ExportMember() bool {
206	// Export the module added via this dependency tag from the sdk.
207	return true
208}
209
210// Avoid having to make stubs content explicitly visible to dependent modules.
211//
212// This is a temporary workaround to make it easier to migrate to bootclasspath_fragment modules
213// with proper dependencies.
214// TODO(b/177892522): Remove this and add needed visibility.
215func (b hiddenAPIStubsDependencyTag) ExcludeFromVisibilityEnforcement() {
216}
217
218var _ android.ExcludeFromVisibilityEnforcementTag = hiddenAPIStubsDependencyTag{}
219var _ android.ReplaceSourceWithPrebuilt = hiddenAPIStubsDependencyTag{}
220var _ android.ExcludeFromApexContentsTag = hiddenAPIStubsDependencyTag{}
221var _ android.SdkMemberTypeDependencyTag = hiddenAPIStubsDependencyTag{}
222
223// hiddenAPIComputeMonolithicStubLibModules computes the set of module names that provide stubs
224// needed to produce the hidden API monolithic stub flags file.
225func hiddenAPIComputeMonolithicStubLibModules(config android.Config) map[*HiddenAPIScope][]string {
226	var publicStubModules []string
227	var systemStubModules []string
228	var testStubModules []string
229	var corePlatformStubModules []string
230
231	if config.AlwaysUsePrebuiltSdks() {
232		// Build configuration mandates using prebuilt stub modules
233		publicStubModules = append(publicStubModules, "sdk_public_current_android")
234		systemStubModules = append(systemStubModules, "sdk_system_current_android")
235		testStubModules = append(testStubModules, "sdk_test_current_android")
236	} else {
237		// Use stub modules built from source
238		publicStubModules = append(publicStubModules, "android_stubs_current")
239		systemStubModules = append(systemStubModules, "android_system_stubs_current")
240		testStubModules = append(testStubModules, "android_test_stubs_current")
241	}
242	// We do not have prebuilts of the core platform api yet
243	corePlatformStubModules = append(corePlatformStubModules, "legacy.core.platform.api.stubs")
244
245	// Allow products to define their own stubs for custom product jars that apps can use.
246	publicStubModules = append(publicStubModules, config.ProductHiddenAPIStubs()...)
247	systemStubModules = append(systemStubModules, config.ProductHiddenAPIStubsSystem()...)
248	testStubModules = append(testStubModules, config.ProductHiddenAPIStubsTest()...)
249	if config.IsEnvTrue("EMMA_INSTRUMENT") {
250		// Add jacoco-stubs to public, system and test. It doesn't make any real difference as public
251		// allows everyone access but it is needed to ensure consistent flags between the
252		// bootclasspath fragment generated flags and the platform_bootclasspath generated flags.
253		publicStubModules = append(publicStubModules, "jacoco-stubs")
254		systemStubModules = append(systemStubModules, "jacoco-stubs")
255		testStubModules = append(testStubModules, "jacoco-stubs")
256	}
257
258	m := map[*HiddenAPIScope][]string{}
259	m[PublicHiddenAPIScope] = publicStubModules
260	m[SystemHiddenAPIScope] = systemStubModules
261	m[TestHiddenAPIScope] = testStubModules
262	m[CorePlatformHiddenAPIScope] = corePlatformStubModules
263	return m
264}
265
266// hiddenAPIAddStubLibDependencies adds dependencies onto the modules specified in
267// apiScopeToStubLibModules. It adds them in a well known order and uses a HiddenAPIScope specific
268// tag to identify the source of the dependency.
269func hiddenAPIAddStubLibDependencies(ctx android.BottomUpMutatorContext, apiScopeToStubLibModules map[*HiddenAPIScope][]string) {
270	module := ctx.Module()
271	for _, apiScope := range hiddenAPIScopes {
272		modules := apiScopeToStubLibModules[apiScope]
273		ctx.AddDependency(module, hiddenAPIStubsDependencyTag{apiScope: apiScope}, modules...)
274	}
275}
276
277// hiddenAPIRetrieveDexJarBuildPath retrieves the DexJarBuildPath from the specified module, if
278// available, or reports an error.
279func hiddenAPIRetrieveDexJarBuildPath(ctx android.ModuleContext, module android.Module, kind android.SdkKind) android.Path {
280	var dexJar android.Path
281	if sdkLibrary, ok := module.(SdkLibraryDependency); ok {
282		dexJar = sdkLibrary.SdkApiStubDexJar(ctx, kind)
283	} else if j, ok := module.(UsesLibraryDependency); ok {
284		dexJar = j.DexJarBuildPath()
285	} else {
286		ctx.ModuleErrorf("dependency %s of module type %s does not support providing a dex jar", module, ctx.OtherModuleType(module))
287		return nil
288	}
289
290	if dexJar == nil {
291		ctx.ModuleErrorf("dependency %s does not provide a dex jar, consider setting compile_dex: true", module)
292	}
293	return dexJar
294}
295
296// buildRuleToGenerateHiddenAPIStubFlagsFile creates a rule to create a hidden API stub flags file.
297//
298// The rule is initialized but not built so that the caller can modify it and select an appropriate
299// name.
300func buildRuleToGenerateHiddenAPIStubFlagsFile(ctx android.BuilderContext, name, desc string, outputPath android.WritablePath, bootDexJars android.Paths, input HiddenAPIFlagInput, moduleStubFlagsPaths android.Paths) {
301	// Singleton rule which applies hiddenapi on all boot class path dex files.
302	rule := android.NewRuleBuilder(pctx, ctx)
303
304	tempPath := tempPathForRestat(ctx, outputPath)
305
306	// Find the widest API stubs provided by the fragments on which this depends, if any.
307	dependencyStubDexJars := input.DependencyStubDexJarsByScope.StubDexJarsForWidestAPIScope()
308
309	// Add widest API stubs from the additional dependencies of this, if any.
310	dependencyStubDexJars = append(dependencyStubDexJars, input.AdditionalStubDexJarsByScope.StubDexJarsForWidestAPIScope()...)
311
312	command := rule.Command().
313		Tool(ctx.Config().HostToolPath(ctx, "hiddenapi")).
314		Text("list").
315		FlagForEachInput("--dependency-stub-dex=", dependencyStubDexJars).
316		FlagForEachInput("--boot-dex=", bootDexJars)
317
318	// If no module stub flags paths are provided then this must be being called for a
319	// bootclasspath_fragment and not the whole platform_bootclasspath.
320	if moduleStubFlagsPaths == nil {
321		// This is being run on a fragment of the bootclasspath.
322		command.Flag("--fragment")
323	}
324
325	// Iterate over the api scopes in a fixed order.
326	for _, apiScope := range hiddenAPIFlagScopes {
327		// Merge in the stub dex jar paths for this api scope from the fragments on which it depends.
328		// They will be needed to resolve dependencies from this fragment's stubs to classes in the
329		// other fragment's APIs.
330		var paths android.Paths
331		paths = append(paths, input.DependencyStubDexJarsByScope.StubDexJarsForScope(apiScope)...)
332		paths = append(paths, input.AdditionalStubDexJarsByScope.StubDexJarsForScope(apiScope)...)
333		paths = append(paths, input.StubDexJarsByScope.StubDexJarsForScope(apiScope)...)
334		if len(paths) > 0 {
335			option := apiScope.hiddenAPIListOption
336			command.FlagWithInputList(option+"=", paths, ":")
337		}
338	}
339
340	// Add the output path.
341	command.FlagWithOutput("--out-api-flags=", tempPath)
342
343	// If there are stub flag files that have been generated by fragments on which this depends then
344	// use them to validate the stub flag file generated by the rules created by this method.
345	if len(moduleStubFlagsPaths) > 0 {
346		validFile := buildRuleValidateOverlappingCsvFiles(ctx, name, desc, outputPath, moduleStubFlagsPaths)
347
348		// Add the file that indicates that the file generated by this is valid.
349		//
350		// This will cause the validation rule above to be run any time that the output of this rule
351		// changes but the validation will run in parallel with other rules that depend on this file.
352		command.Validation(validFile)
353	}
354
355	commitChangeForRestat(rule, tempPath, outputPath)
356
357	rule.Build(name, desc)
358}
359
360// HiddenAPIFlagFileProperties contains paths to the flag files that can be used to augment the
361// information obtained from annotations within the source code in order to create the complete set
362// of flags that should be applied to the dex implementation jars on the bootclasspath.
363//
364// Each property contains a list of paths. With the exception of the Unsupported_packages the paths
365// of each property reference a plain text file that contains a java signature per line. The flags
366// for each of those signatures will be updated in a property specific way.
367//
368// The Unsupported_packages property contains a list of paths, each of which is a plain text file
369// with one Java package per line. All members of all classes within that package (but not nested
370// packages) will be updated in a property specific way.
371type HiddenAPIFlagFileProperties struct {
372	// Marks each signature in the referenced files as being unsupported.
373	Unsupported []string `android:"path"`
374
375	// Marks each signature in the referenced files as being unsupported because it has been removed.
376	// Any conflicts with other flags are ignored.
377	Removed []string `android:"path"`
378
379	// Marks each signature in the referenced files as being supported only for targetSdkVersion <= R
380	// and low priority.
381	Max_target_r_low_priority []string `android:"path"`
382
383	// Marks each signature in the referenced files as being supported only for targetSdkVersion <= Q.
384	Max_target_q []string `android:"path"`
385
386	// Marks each signature in the referenced files as being supported only for targetSdkVersion <= P.
387	Max_target_p []string `android:"path"`
388
389	// Marks each signature in the referenced files as being supported only for targetSdkVersion <= O
390	// and low priority. Any conflicts with other flags are ignored.
391	Max_target_o_low_priority []string `android:"path"`
392
393	// Marks each signature in the referenced files as being blocked.
394	Blocked []string `android:"path"`
395
396	// Marks each signature in every package in the referenced files as being unsupported.
397	Unsupported_packages []string `android:"path"`
398}
399
400type hiddenAPIFlagFileCategory struct {
401	// PropertyName is the name of the property for this category.
402	PropertyName string
403
404	// propertyValueReader retrieves the value of the property for this category from the set of
405	// properties.
406	propertyValueReader func(properties *HiddenAPIFlagFileProperties) []string
407
408	// commandMutator adds the appropriate command line options for this category to the supplied
409	// command
410	commandMutator func(command *android.RuleBuilderCommand, path android.Path)
411}
412
413// The flag file category for removed members of the API.
414//
415// This is extracted from HiddenAPIFlagFileCategories as it is needed to add the dex signatures
416// list of removed API members that are generated automatically from the removed.txt files provided
417// by API stubs.
418var hiddenAPIRemovedFlagFileCategory = &hiddenAPIFlagFileCategory{
419	// See HiddenAPIFlagFileProperties.Removed
420	PropertyName: "removed",
421	propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string {
422		return properties.Removed
423	},
424	commandMutator: func(command *android.RuleBuilderCommand, path android.Path) {
425		command.FlagWithInput("--unsupported ", path).Flag("--ignore-conflicts ").FlagWithArg("--tag ", "removed")
426	},
427}
428
429var HiddenAPIFlagFileCategories = []*hiddenAPIFlagFileCategory{
430	// See HiddenAPIFlagFileProperties.Unsupported
431	{
432		PropertyName: "unsupported",
433		propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string {
434			return properties.Unsupported
435		},
436		commandMutator: func(command *android.RuleBuilderCommand, path android.Path) {
437			command.FlagWithInput("--unsupported ", path)
438		},
439	},
440	hiddenAPIRemovedFlagFileCategory,
441	// See HiddenAPIFlagFileProperties.Max_target_r_low_priority
442	{
443		PropertyName: "max_target_r_low_priority",
444		propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string {
445			return properties.Max_target_r_low_priority
446		},
447		commandMutator: func(command *android.RuleBuilderCommand, path android.Path) {
448			command.FlagWithInput("--max-target-r ", path).FlagWithArg("--tag ", "lo-prio")
449		},
450	},
451	// See HiddenAPIFlagFileProperties.Max_target_q
452	{
453		PropertyName: "max_target_q",
454		propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string {
455			return properties.Max_target_q
456		},
457		commandMutator: func(command *android.RuleBuilderCommand, path android.Path) {
458			command.FlagWithInput("--max-target-q ", path)
459		},
460	},
461	// See HiddenAPIFlagFileProperties.Max_target_p
462	{
463		PropertyName: "max_target_p",
464		propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string {
465			return properties.Max_target_p
466		},
467		commandMutator: func(command *android.RuleBuilderCommand, path android.Path) {
468			command.FlagWithInput("--max-target-p ", path)
469		},
470	},
471	// See HiddenAPIFlagFileProperties.Max_target_o_low_priority
472	{
473		PropertyName: "max_target_o_low_priority",
474		propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string {
475			return properties.Max_target_o_low_priority
476		},
477		commandMutator: func(command *android.RuleBuilderCommand, path android.Path) {
478			command.FlagWithInput("--max-target-o ", path).Flag("--ignore-conflicts ").FlagWithArg("--tag ", "lo-prio")
479		},
480	},
481	// See HiddenAPIFlagFileProperties.Blocked
482	{
483		PropertyName: "blocked",
484		propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string {
485			return properties.Blocked
486		},
487		commandMutator: func(command *android.RuleBuilderCommand, path android.Path) {
488			command.FlagWithInput("--blocked ", path)
489		},
490	},
491	// See HiddenAPIFlagFileProperties.Unsupported_packages
492	{
493		PropertyName: "unsupported_packages",
494		propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string {
495			return properties.Unsupported_packages
496		},
497		commandMutator: func(command *android.RuleBuilderCommand, path android.Path) {
498			command.FlagWithInput("--unsupported ", path).Flag("--packages ")
499		},
500	},
501}
502
503// FlagFilesByCategory maps a hiddenAPIFlagFileCategory to the paths to the files in that category.
504type FlagFilesByCategory map[*hiddenAPIFlagFileCategory]android.Paths
505
506// append appends the supplied flags files to the corresponding category in this map.
507func (s FlagFilesByCategory) append(other FlagFilesByCategory) {
508	for _, category := range HiddenAPIFlagFileCategories {
509		s[category] = append(s[category], other[category]...)
510	}
511}
512
513// dedup removes duplicates in the flag files, while maintaining the order in which they were
514// appended.
515func (s FlagFilesByCategory) dedup() {
516	for category, paths := range s {
517		s[category] = android.FirstUniquePaths(paths)
518	}
519}
520
521// HiddenAPIInfo contains information provided by the hidden API processing.
522//
523// That includes paths resolved from HiddenAPIFlagFileProperties and also generated by hidden API
524// processing.
525type HiddenAPIInfo struct {
526	// FlagFilesByCategory maps from the flag file category to the paths containing information for
527	// that category.
528	FlagFilesByCategory FlagFilesByCategory
529
530	// The paths to the stub dex jars for each of the *HiddenAPIScope in hiddenAPIScopes provided by
531	// this fragment and the fragments on which this depends.
532	TransitiveStubDexJarsByScope StubDexJarsByModule
533
534	// The output from the hidden API processing needs to be made available to other modules.
535	HiddenAPIFlagOutput
536}
537
538func newHiddenAPIInfo() *HiddenAPIInfo {
539	info := HiddenAPIInfo{
540		FlagFilesByCategory:          FlagFilesByCategory{},
541		TransitiveStubDexJarsByScope: StubDexJarsByModule{},
542	}
543	return &info
544}
545
546func (i *HiddenAPIInfo) mergeFromFragmentDeps(ctx android.ModuleContext, fragments []android.Module) {
547	// Merge all the information from the fragments. The fragments form a DAG so it is possible that
548	// this will introduce duplicates so they will be resolved after processing all the fragments.
549	for _, fragment := range fragments {
550		if ctx.OtherModuleHasProvider(fragment, HiddenAPIInfoProvider) {
551			info := ctx.OtherModuleProvider(fragment, HiddenAPIInfoProvider).(HiddenAPIInfo)
552			i.TransitiveStubDexJarsByScope.addStubDexJarsByModule(info.TransitiveStubDexJarsByScope)
553		}
554	}
555}
556
557var HiddenAPIInfoProvider = blueprint.NewProvider(HiddenAPIInfo{})
558
559// ModuleStubDexJars contains the stub dex jars provided by a single module.
560//
561// It maps a *HiddenAPIScope to the path to stub dex jars appropriate for that scope. See
562// hiddenAPIScopes for a list of the acceptable *HiddenAPIScope values.
563type ModuleStubDexJars map[*HiddenAPIScope]android.Path
564
565// stubDexJarForWidestAPIScope returns the stub dex jars for the widest API scope provided by this
566// map.
567//
568// The relative width of APIs is determined by their order in hiddenAPIScopes.
569func (s ModuleStubDexJars) stubDexJarForWidestAPIScope() android.Path {
570	for i := len(hiddenAPIScopes) - 1; i >= 0; i-- {
571		apiScope := hiddenAPIScopes[i]
572		if stubsForAPIScope, ok := s[apiScope]; ok {
573			return stubsForAPIScope
574		}
575	}
576
577	return nil
578}
579
580// StubDexJarsByModule contains the stub dex jars provided by a set of modules.
581//
582// It maps a module name to the path to the stub dex jars provided by that module.
583type StubDexJarsByModule map[string]ModuleStubDexJars
584
585// addStubDexJar adds a stub dex jar path provided by the specified module for the specified scope.
586func (s StubDexJarsByModule) addStubDexJar(ctx android.ModuleContext, module android.Module, scope *HiddenAPIScope, stubDexJar android.Path) {
587	name := android.RemoveOptionalPrebuiltPrefix(module.Name())
588
589	// Each named module provides one dex jar for each scope. However, in some cases different API
590	// versions of a single classes are provided by separate modules. e.g. the core platform
591	// version of java.lang.Object is provided by the legacy.art.module.platform.api module but the
592	// public version is provided by the art.module.public.api module. In those cases it is necessary
593	// to treat all those modules as they were the same name, otherwise it will result in multiple
594	// definitions of a single class being passed to hidden API processing which will cause an error.
595	if name == scope.nonUpdatablePrebuiltModule || name == scope.nonUpdatableSourceModule {
596		// Treat all *android-non-updatable* modules as if they were part of an android-non-updatable
597		// java_sdk_library.
598		// TODO(b/192067200): Remove once android-non-updatable is a java_sdk_library or equivalent.
599		name = "android-non-updatable"
600	} else if name == "legacy.art.module.platform.api" {
601		// Treat legacy.art.module.platform.api as if it was an API scope provided by the
602		// art.module.public.api java_sdk_library which will be the case once the former has been
603		// migrated to a module_lib API.
604		name = "art.module.public.api"
605	} else if name == "legacy.i18n.module.platform.api" {
606		// Treat legacy.i18n.module.platform.api as if it was an API scope provided by the
607		// i18n.module.public.api java_sdk_library which will be the case once the former has been
608		// migrated to a module_lib API.
609		name = "i18n.module.public.api"
610	} else if name == "conscrypt.module.platform.api" {
611		// Treat conscrypt.module.platform.api as if it was an API scope provided by the
612		// conscrypt.module.public.api java_sdk_library which will be the case once the former has been
613		// migrated to a module_lib API.
614		name = "conscrypt.module.public.api"
615	} else if d, ok := module.(SdkLibraryComponentDependency); ok {
616		sdkLibraryName := d.SdkLibraryName()
617		if sdkLibraryName != nil {
618			// The module is a component of a java_sdk_library so use the name of the java_sdk_library.
619			// e.g. if this module is `foo.system.stubs` and is part of the `foo` java_sdk_library then
620			// use `foo` as the name.
621			name = *sdkLibraryName
622		}
623	}
624	stubDexJarsByScope := s[name]
625	if stubDexJarsByScope == nil {
626		stubDexJarsByScope = ModuleStubDexJars{}
627		s[name] = stubDexJarsByScope
628	}
629	stubDexJarsByScope[scope] = stubDexJar
630}
631
632// addStubDexJarsByModule adds the stub dex jars in the supplied StubDexJarsByModule to this map.
633func (s StubDexJarsByModule) addStubDexJarsByModule(other StubDexJarsByModule) {
634	for module, stubDexJarsByScope := range other {
635		s[module] = stubDexJarsByScope
636	}
637}
638
639// StubDexJarsForWidestAPIScope returns a list of stub dex jars containing the widest API scope
640// provided by each module.
641//
642// The relative width of APIs is determined by their order in hiddenAPIScopes.
643func (s StubDexJarsByModule) StubDexJarsForWidestAPIScope() android.Paths {
644	stubDexJars := android.Paths{}
645	modules := android.SortedStringKeys(s)
646	for _, module := range modules {
647		stubDexJarsByScope := s[module]
648
649		stubDexJars = append(stubDexJars, stubDexJarsByScope.stubDexJarForWidestAPIScope())
650	}
651
652	return stubDexJars
653}
654
655// StubDexJarsForScope returns a list of stub dex jars containing the stub dex jars provided by each
656// module for the specified scope.
657//
658// If a module does not provide a stub dex jar for the supplied scope then it does not contribute to
659// the returned list.
660func (s StubDexJarsByModule) StubDexJarsForScope(scope *HiddenAPIScope) android.Paths {
661	stubDexJars := android.Paths{}
662	modules := android.SortedStringKeys(s)
663	for _, module := range modules {
664		stubDexJarsByScope := s[module]
665		// Not every module will have the same set of
666		if jars, ok := stubDexJarsByScope[scope]; ok {
667			stubDexJars = append(stubDexJars, jars)
668		}
669	}
670
671	return stubDexJars
672}
673
674// HiddenAPIFlagInput encapsulates information obtained from a module and its dependencies that are
675// needed for hidden API flag generation.
676type HiddenAPIFlagInput struct {
677	// FlagFilesByCategory contains the flag files that override the initial flags that are derived
678	// from the stub dex files.
679	FlagFilesByCategory FlagFilesByCategory
680
681	// StubDexJarsByScope contains the stub dex jars for different *HiddenAPIScope and which determine
682	// the initial flags for each dex member.
683	StubDexJarsByScope StubDexJarsByModule
684
685	// DependencyStubDexJarsByScope contains the stub dex jars provided by the fragments on which this
686	// depends. It is the result of merging HiddenAPIInfo.TransitiveStubDexJarsByScope from each
687	// fragment on which this depends.
688	DependencyStubDexJarsByScope StubDexJarsByModule
689
690	// AdditionalStubDexJarsByScope contains stub dex jars provided by other modules in addition to
691	// the ones that are obtained from fragments on which this depends.
692	//
693	// These are kept separate from stub dex jars in HiddenAPIFlagInput.DependencyStubDexJarsByScope
694	// as there are not propagated transitively to other fragments that depend on this.
695	AdditionalStubDexJarsByScope StubDexJarsByModule
696
697	// RemovedTxtFiles is the list of removed.txt files provided by java_sdk_library modules that are
698	// specified in the bootclasspath_fragment's stub_libs and contents properties.
699	RemovedTxtFiles android.Paths
700}
701
702// newHiddenAPIFlagInput creates a new initialize HiddenAPIFlagInput struct.
703func newHiddenAPIFlagInput() HiddenAPIFlagInput {
704	input := HiddenAPIFlagInput{
705		FlagFilesByCategory:          FlagFilesByCategory{},
706		StubDexJarsByScope:           StubDexJarsByModule{},
707		DependencyStubDexJarsByScope: StubDexJarsByModule{},
708		AdditionalStubDexJarsByScope: StubDexJarsByModule{},
709	}
710
711	return input
712}
713
714// gatherStubLibInfo gathers information from the stub libs needed by hidden API processing from the
715// dependencies added in hiddenAPIAddStubLibDependencies.
716//
717// That includes paths to the stub dex jars as well as paths to the *removed.txt files.
718func (i *HiddenAPIFlagInput) gatherStubLibInfo(ctx android.ModuleContext, contents []android.Module) {
719	addFromModule := func(ctx android.ModuleContext, module android.Module, apiScope *HiddenAPIScope) {
720		sdkKind := apiScope.sdkKind
721		dexJar := hiddenAPIRetrieveDexJarBuildPath(ctx, module, sdkKind)
722		if dexJar != nil {
723			i.StubDexJarsByScope.addStubDexJar(ctx, module, apiScope, dexJar)
724		}
725
726		if sdkLibrary, ok := module.(SdkLibraryDependency); ok {
727			removedTxtFile := sdkLibrary.SdkRemovedTxtFile(ctx, sdkKind)
728			i.RemovedTxtFiles = append(i.RemovedTxtFiles, removedTxtFile.AsPaths()...)
729		}
730	}
731
732	// If the contents includes any java_sdk_library modules then add them to the stubs.
733	for _, module := range contents {
734		if _, ok := module.(SdkLibraryDependency); ok {
735			// Add information for every possible API scope needed by hidden API.
736			for _, apiScope := range hiddenAPISdkLibrarySupportedScopes {
737				addFromModule(ctx, module, apiScope)
738			}
739		}
740	}
741
742	ctx.VisitDirectDeps(func(module android.Module) {
743		tag := ctx.OtherModuleDependencyTag(module)
744		if hiddenAPIStubsTag, ok := tag.(hiddenAPIStubsDependencyTag); ok {
745			apiScope := hiddenAPIStubsTag.apiScope
746			if hiddenAPIStubsTag.fromAdditionalDependency {
747				dexJar := hiddenAPIRetrieveDexJarBuildPath(ctx, module, apiScope.sdkKind)
748				if dexJar != nil {
749					i.AdditionalStubDexJarsByScope.addStubDexJar(ctx, module, apiScope, dexJar)
750				}
751			} else {
752				addFromModule(ctx, module, apiScope)
753			}
754		}
755	})
756
757	// Normalize the paths, i.e. remove duplicates and sort.
758	i.RemovedTxtFiles = android.SortedUniquePaths(i.RemovedTxtFiles)
759}
760
761// extractFlagFilesFromProperties extracts the paths to flag files that are specified in the
762// supplied properties and stores them in this struct.
763func (i *HiddenAPIFlagInput) extractFlagFilesFromProperties(ctx android.ModuleContext, p *HiddenAPIFlagFileProperties) {
764	for _, category := range HiddenAPIFlagFileCategories {
765		paths := android.PathsForModuleSrc(ctx, category.propertyValueReader(p))
766		i.FlagFilesByCategory[category] = paths
767	}
768}
769
770func (i *HiddenAPIFlagInput) transitiveStubDexJarsByScope() StubDexJarsByModule {
771	transitive := i.DependencyStubDexJarsByScope
772	transitive.addStubDexJarsByModule(i.StubDexJarsByScope)
773	return transitive
774}
775
776// HiddenAPIFlagOutput contains paths to output files from the hidden API flag generation for a
777// bootclasspath_fragment module.
778type HiddenAPIFlagOutput struct {
779	// The path to the generated stub-flags.csv file.
780	StubFlagsPath android.Path
781
782	// The path to the generated annotation-flags.csv file.
783	AnnotationFlagsPath android.Path
784
785	// The path to the generated metadata.csv file.
786	MetadataPath android.Path
787
788	// The path to the generated index.csv file.
789	IndexPath android.Path
790
791	// The path to the generated all-flags.csv file.
792	AllFlagsPath android.Path
793}
794
795// bootDexJarByModule is a map from base module name (without prebuilt_ prefix) to the boot dex
796// path.
797type bootDexJarByModule map[string]android.Path
798
799// addPath adds the path for a module to the map.
800func (b bootDexJarByModule) addPath(module android.Module, path android.Path) {
801	b[android.RemoveOptionalPrebuiltPrefix(module.Name())] = path
802}
803
804// bootDexJars returns the boot dex jar paths sorted by their keys.
805func (b bootDexJarByModule) bootDexJars() android.Paths {
806	paths := android.Paths{}
807	for _, k := range android.SortedStringKeys(b) {
808		paths = append(paths, b[k])
809	}
810	return paths
811}
812
813// bootDexJarsWithoutCoverage returns the boot dex jar paths sorted by their keys without coverage
814// libraries if present.
815func (b bootDexJarByModule) bootDexJarsWithoutCoverage() android.Paths {
816	paths := android.Paths{}
817	for _, k := range android.SortedStringKeys(b) {
818		if k == "jacocoagent" {
819			continue
820		}
821		paths = append(paths, b[k])
822	}
823	return paths
824}
825
826// HiddenAPIOutput encapsulates the output from the hidden API processing.
827type HiddenAPIOutput struct {
828	HiddenAPIFlagOutput
829
830	// The map from base module name to the path to the encoded boot dex file.
831	EncodedBootDexFilesByModule bootDexJarByModule
832}
833
834// pathForValidation creates a path of the same type as the supplied type but with a name of
835// <path>.valid.
836//
837// e.g. If path is an OutputPath for out/soong/hiddenapi/hiddenapi-flags.csv then this will return
838// an OutputPath for out/soong/hiddenapi/hiddenapi-flags.csv.valid
839func pathForValidation(ctx android.PathContext, path android.WritablePath) android.WritablePath {
840	extWithoutLeadingDot := strings.TrimPrefix(path.Ext(), ".")
841	return path.ReplaceExtension(ctx, extWithoutLeadingDot+".valid")
842}
843
844// buildRuleToGenerateHiddenApiFlags creates a rule to create the monolithic hidden API flags from
845// the flags from all the modules, the stub flags, augmented with some additional configuration
846// files.
847//
848// baseFlagsPath is the path to the flags file containing all the information from the stubs plus
849// an entry for every single member in the dex implementation jars of the individual modules. Every
850// signature in any of the other files MUST be included in this file.
851//
852// annotationFlags is the path to the annotation flags file generated from annotation information
853// in each module.
854//
855// hiddenAPIInfo is a struct containing paths to files that augment the information provided by
856// the annotationFlags.
857func buildRuleToGenerateHiddenApiFlags(ctx android.BuilderContext, name, desc string,
858	outputPath android.WritablePath, baseFlagsPath android.Path, annotationFlagPaths android.Paths,
859	flagFilesByCategory FlagFilesByCategory, allFlagsPaths android.Paths, generatedRemovedDexSignatures android.OptionalPath) {
860
861	// Create the rule that will generate the flag files.
862	tempPath := tempPathForRestat(ctx, outputPath)
863	rule := android.NewRuleBuilder(pctx, ctx)
864	command := rule.Command().
865		BuiltTool("generate_hiddenapi_lists").
866		FlagWithInput("--csv ", baseFlagsPath).
867		Inputs(annotationFlagPaths).
868		FlagWithOutput("--output ", tempPath)
869
870	// Add the options for the different categories of flag files.
871	for _, category := range HiddenAPIFlagFileCategories {
872		paths := flagFilesByCategory[category]
873		for _, path := range paths {
874			category.commandMutator(command, path)
875		}
876	}
877
878	// If available then pass the automatically generated file containing dex signatures of removed
879	// API members to the rule so they can be marked as removed.
880	if generatedRemovedDexSignatures.Valid() {
881		hiddenAPIRemovedFlagFileCategory.commandMutator(command, generatedRemovedDexSignatures.Path())
882	}
883
884	commitChangeForRestat(rule, tempPath, outputPath)
885
886	// If there are flag files that have been generated by fragments on which this depends then use
887	// them to validate the flag file generated by the rules created by this method.
888	if len(allFlagsPaths) > 0 {
889		validFile := buildRuleValidateOverlappingCsvFiles(ctx, name, desc, outputPath, allFlagsPaths)
890
891		// Add the file that indicates that the file generated by this is valid.
892		//
893		// This will cause the validation rule above to be run any time that the output of this rule
894		// changes but the validation will run in parallel with other rules that depend on this file.
895		command.Validation(validFile)
896	}
897
898	rule.Build(name, desc)
899}
900
901// buildRuleValidateOverlappingCsvFiles checks that the modular CSV files, i.e. the files generated
902// by the individual bootclasspath_fragment modules are subsets of the monolithic CSV file.
903func buildRuleValidateOverlappingCsvFiles(ctx android.BuilderContext, name string, desc string, monolithicFilePath android.WritablePath, modularFilePaths android.Paths) android.WritablePath {
904	// The file which is used to record that the flags file is valid.
905	validFile := pathForValidation(ctx, monolithicFilePath)
906
907	// Create a rule to validate the output from the following rule.
908	rule := android.NewRuleBuilder(pctx, ctx)
909	rule.Command().
910		BuiltTool("verify_overlaps").
911		Input(monolithicFilePath).
912		Inputs(modularFilePaths).
913		// If validation passes then update the file that records that.
914		Text("&& touch").Output(validFile)
915	rule.Build(name+"Validation", desc+" validation")
916
917	return validFile
918}
919
920// hiddenAPIRulesForBootclasspathFragment will generate all the flags for a fragment of the
921// bootclasspath and then encode the flags into the boot dex files.
922//
923// It takes:
924// * Map from android.SdkKind to stub dex jar paths defining the API for that sdk kind.
925// * The list of modules that are the contents of the fragment.
926// * The additional manually curated flag files to use.
927//
928// It generates:
929// * stub-flags.csv
930// * annotation-flags.csv
931// * metadata.csv
932// * index.csv
933// * all-flags.csv
934// * encoded boot dex files
935func hiddenAPIRulesForBootclasspathFragment(ctx android.ModuleContext, contents []android.Module, input HiddenAPIFlagInput) *HiddenAPIOutput {
936	hiddenApiSubDir := "modular-hiddenapi"
937
938	// Gather information about the boot dex files for the boot libraries provided by this fragment.
939	bootDexInfoByModule := extractBootDexInfoFromModules(ctx, contents)
940
941	// Generate the stub-flags.csv.
942	stubFlagsCSV := android.PathForModuleOut(ctx, hiddenApiSubDir, "stub-flags.csv")
943	buildRuleToGenerateHiddenAPIStubFlagsFile(ctx, "modularHiddenAPIStubFlagsFile", "modular hiddenapi stub flags", stubFlagsCSV, bootDexInfoByModule.bootDexJars(), input, nil)
944
945	// Extract the classes jars from the contents.
946	classesJars := extractClassesJarsFromModules(contents)
947
948	// Generate the set of flags from the annotations in the source code.
949	annotationFlagsCSV := android.PathForModuleOut(ctx, hiddenApiSubDir, "annotation-flags.csv")
950	buildRuleToGenerateAnnotationFlags(ctx, "modular hiddenapi annotation flags", classesJars, stubFlagsCSV, annotationFlagsCSV)
951
952	// Generate the metadata from the annotations in the source code.
953	metadataCSV := android.PathForModuleOut(ctx, hiddenApiSubDir, "metadata.csv")
954	buildRuleToGenerateMetadata(ctx, "modular hiddenapi metadata", classesJars, stubFlagsCSV, metadataCSV)
955
956	// Generate the index file from the CSV files in the classes jars.
957	indexCSV := android.PathForModuleOut(ctx, hiddenApiSubDir, "index.csv")
958	buildRuleToGenerateIndex(ctx, "modular hiddenapi index", classesJars, indexCSV)
959
960	// Removed APIs need to be marked and in order to do that the hiddenAPIInfo needs to specify files
961	// containing dex signatures of all the removed APIs. In the monolithic files that is done by
962	// manually combining all the removed.txt files for each API and then converting them to dex
963	// signatures, see the combined-removed-dex module. This does that automatically by using the
964	// *removed.txt files retrieved from the java_sdk_library modules that are specified in the
965	// stub_libs and contents properties of a bootclasspath_fragment.
966	removedDexSignatures := buildRuleToGenerateRemovedDexSignatures(ctx, input.RemovedTxtFiles)
967
968	// Generate the all-flags.csv which are the flags that will, in future, be encoded into the dex
969	// files.
970	allFlagsCSV := android.PathForModuleOut(ctx, hiddenApiSubDir, "all-flags.csv")
971	buildRuleToGenerateHiddenApiFlags(ctx, "modularHiddenApiAllFlags", "modular hiddenapi all flags", allFlagsCSV, stubFlagsCSV, android.Paths{annotationFlagsCSV}, input.FlagFilesByCategory, nil, removedDexSignatures)
972
973	// Encode the flags into the boot dex files.
974	encodedBootDexJarsByModule := map[string]android.Path{}
975	outputDir := android.PathForModuleOut(ctx, "hiddenapi-modular/encoded").OutputPath
976	for _, name := range android.SortedStringKeys(bootDexInfoByModule) {
977		bootDexInfo := bootDexInfoByModule[name]
978		unencodedDex := bootDexInfo.path
979		encodedDex := hiddenAPIEncodeDex(ctx, unencodedDex, allFlagsCSV, bootDexInfo.uncompressDex, outputDir)
980		encodedBootDexJarsByModule[name] = encodedDex
981	}
982
983	// Store the paths in the info for use by other modules and sdk snapshot generation.
984	output := HiddenAPIOutput{
985		HiddenAPIFlagOutput: HiddenAPIFlagOutput{
986			StubFlagsPath:       stubFlagsCSV,
987			AnnotationFlagsPath: annotationFlagsCSV,
988			MetadataPath:        metadataCSV,
989			IndexPath:           indexCSV,
990			AllFlagsPath:        allFlagsCSV,
991		},
992		EncodedBootDexFilesByModule: encodedBootDexJarsByModule,
993	}
994	return &output
995}
996
997func buildRuleToGenerateRemovedDexSignatures(ctx android.ModuleContext, removedTxtFiles android.Paths) android.OptionalPath {
998	if len(removedTxtFiles) == 0 {
999		return android.OptionalPath{}
1000	}
1001
1002	output := android.PathForModuleOut(ctx, "modular-hiddenapi/removed-dex-signatures.txt")
1003
1004	rule := android.NewRuleBuilder(pctx, ctx)
1005	rule.Command().
1006		BuiltTool("metalava").
1007		Flag("--no-banner").
1008		Inputs(removedTxtFiles).
1009		FlagWithOutput("--dex-api ", output)
1010	rule.Build("modular-hiddenapi-removed-dex-signatures", "modular hiddenapi removed dex signatures")
1011	return android.OptionalPathForPath(output)
1012}
1013
1014// extractBootDexJarsFromModules extracts the boot dex jars from the supplied modules.
1015func extractBootDexJarsFromModules(ctx android.ModuleContext, contents []android.Module) bootDexJarByModule {
1016	bootDexJars := bootDexJarByModule{}
1017	for _, module := range contents {
1018		hiddenAPIModule := hiddenAPIModuleFromModule(ctx, module)
1019		if hiddenAPIModule == nil {
1020			continue
1021		}
1022		bootDexJar := retrieveBootDexJarFromHiddenAPIModule(ctx, hiddenAPIModule)
1023		bootDexJars.addPath(module, bootDexJar)
1024	}
1025	return bootDexJars
1026}
1027
1028func hiddenAPIModuleFromModule(ctx android.BaseModuleContext, module android.Module) hiddenAPIModule {
1029	if hiddenAPIModule, ok := module.(hiddenAPIModule); ok {
1030		return hiddenAPIModule
1031	} else if _, ok := module.(*DexImport); ok {
1032		// Ignore this for the purposes of hidden API processing
1033	} else {
1034		ctx.ModuleErrorf("module %s does not implement hiddenAPIModule", module)
1035	}
1036
1037	return nil
1038}
1039
1040// bootDexInfo encapsulates both the path and uncompressDex status retrieved from a hiddenAPIModule.
1041type bootDexInfo struct {
1042	// The path to the dex jar that has not had hidden API flags encoded into it.
1043	path android.Path
1044
1045	// Indicates whether the dex jar needs uncompressing before encoding.
1046	uncompressDex bool
1047}
1048
1049// bootDexInfoByModule is a map from module name (as returned by module.Name()) to the boot dex
1050// path (as returned by hiddenAPIModule.bootDexJar()) and the uncompressDex flag.
1051type bootDexInfoByModule map[string]bootDexInfo
1052
1053// bootDexJars returns the boot dex jar paths sorted by their keys.
1054func (b bootDexInfoByModule) bootDexJars() android.Paths {
1055	paths := android.Paths{}
1056	for _, m := range android.SortedStringKeys(b) {
1057		paths = append(paths, b[m].path)
1058	}
1059	return paths
1060}
1061
1062// extractBootDexInfoFromModules extracts the boot dex jar and uncompress dex state from
1063// each of the supplied modules which must implement hiddenAPIModule.
1064func extractBootDexInfoFromModules(ctx android.ModuleContext, contents []android.Module) bootDexInfoByModule {
1065	bootDexJarsByModule := bootDexInfoByModule{}
1066	for _, module := range contents {
1067		hiddenAPIModule := module.(hiddenAPIModule)
1068		bootDexJar := retrieveBootDexJarFromHiddenAPIModule(ctx, hiddenAPIModule)
1069		bootDexJarsByModule[module.Name()] = bootDexInfo{
1070			path:          bootDexJar,
1071			uncompressDex: *hiddenAPIModule.uncompressDex(),
1072		}
1073	}
1074
1075	return bootDexJarsByModule
1076}
1077
1078// retrieveBootDexJarFromHiddenAPIModule retrieves the boot dex jar from the hiddenAPIModule.
1079//
1080// If the module does not provide a boot dex jar, i.e. the returned boot dex jar is nil, then  that
1081// create a fake path and either report an error immediately or defer reporting of the error until
1082// the path is actually used.
1083func retrieveBootDexJarFromHiddenAPIModule(ctx android.ModuleContext, module hiddenAPIModule) android.Path {
1084	bootDexJar := module.bootDexJar()
1085	if bootDexJar == nil {
1086		fake := android.PathForModuleOut(ctx, fmt.Sprintf("fake/boot-dex/%s.jar", module.Name()))
1087		bootDexJar = fake
1088
1089		handleMissingDexBootFile(ctx, module, fake)
1090	}
1091	return bootDexJar
1092}
1093
1094// extractClassesJarsFromModules extracts the class jars from the supplied modules.
1095func extractClassesJarsFromModules(contents []android.Module) android.Paths {
1096	classesJars := android.Paths{}
1097	for _, module := range contents {
1098		classesJars = append(classesJars, retrieveClassesJarsFromModule(module)...)
1099	}
1100	return classesJars
1101}
1102
1103// retrieveClassesJarsFromModule retrieves the classes jars from the supplied module.
1104func retrieveClassesJarsFromModule(module android.Module) android.Paths {
1105	if hiddenAPIModule, ok := module.(hiddenAPIModule); ok {
1106		return hiddenAPIModule.classesJars()
1107	}
1108
1109	return nil
1110}
1111
1112// deferReportingMissingBootDexJar returns true if a missing boot dex jar should not be reported by
1113// Soong but should instead only be reported in ninja if the file is actually built.
1114func deferReportingMissingBootDexJar(ctx android.ModuleContext, module android.Module) bool {
1115	// TODO(b/179354495): Remove this workaround when it is unnecessary.
1116	// Prebuilt modules like framework-wifi do not yet provide dex implementation jars. So,
1117	// create a fake one that will cause a build error only if it is used.
1118	if ctx.Config().AlwaysUsePrebuiltSdks() {
1119		return true
1120	}
1121
1122	// Any missing dependency should be allowed.
1123	if ctx.Config().AllowMissingDependencies() {
1124		return true
1125	}
1126
1127	// A bootclasspath module that is part of a versioned sdk never provides a boot dex jar as there
1128	// is no equivalently versioned prebuilt APEX file from which it can be obtained. However,
1129	// versioned bootclasspath modules are processed by Soong so in order to avoid them causing build
1130	// failures missing boot dex jars need to be deferred.
1131	if android.IsModuleInVersionedSdk(ctx.Module()) {
1132		return true
1133	}
1134
1135	// This is called for both platform_bootclasspath and bootclasspath_fragment modules.
1136	//
1137	// A bootclasspath_fragment module should only use the APEX variant of source or prebuilt modules.
1138	// Ideally, a bootclasspath_fragment module should never have a platform variant created for it
1139	// but unfortunately, due to b/187910671 it does.
1140	//
1141	// That causes issues when obtaining a boot dex jar for a prebuilt module as a prebuilt module
1142	// used by a bootclasspath_fragment can only provide a boot dex jar when it is part of APEX, i.e.
1143	// has an APEX variant not a platform variant.
1144	//
1145	// There are some other situations when a prebuilt module used by a bootclasspath_fragment cannot
1146	// provide a boot dex jar:
1147	// 1. If the bootclasspath_fragment is not exported by the prebuilt_apex/apex_set module then it
1148	//    does not have an APEX variant and only has a platform variant and neither do its content
1149	//    modules.
1150	// 2. Some build configurations, e.g. setting TARGET_BUILD_USE_PREBUILT_SDKS causes all
1151	//    java_sdk_library_import modules to be treated as preferred and as many of them are not part
1152	//    of an apex they cannot provide a boot dex jar.
1153	//
1154	// The first case causes problems when the affected prebuilt modules are preferred but that is an
1155	// invalid configuration and it is ok for it to fail as the work to enable that is not yet
1156	// complete. The second case is used for building targets that do not use boot dex jars and so
1157	// deferring error reporting to ninja is fine as the affected ninja targets should never be built.
1158	// That is handled above.
1159	//
1160	// A platform_bootclasspath module can use libraries from both platform and APEX variants. Unlike
1161	// the bootclasspath_fragment it supports dex_import modules which provides the dex file. So, it
1162	// can obtain a boot dex jar from a prebuilt that is not part of an APEX. However, it is assumed
1163	// that if the library can be part of an APEX then it is the APEX variant that is used.
1164	//
1165	// This check handles the slightly different requirements of the bootclasspath_fragment and
1166	// platform_bootclasspath modules by only deferring error reporting for the platform variant of
1167	// a prebuilt modules that has other variants which are part of an APEX.
1168	//
1169	// TODO(b/187910671): Remove this once platform variants are no longer created unnecessarily.
1170	if android.IsModulePrebuilt(module) {
1171		// An inactive source module can still contribute to the APEX but an inactive prebuilt module
1172		// should not contribute to anything. So, rather than have a missing dex jar cause a Soong
1173		// failure defer the error reporting to Ninja. Unless the prebuilt build target is explicitly
1174		// built Ninja should never use the dex jar file.
1175		if !isActiveModule(module) {
1176			return true
1177		}
1178
1179		if am, ok := module.(android.ApexModule); ok && am.InAnyApex() {
1180			apexInfo := ctx.OtherModuleProvider(module, android.ApexInfoProvider).(android.ApexInfo)
1181			if apexInfo.IsForPlatform() {
1182				return true
1183			}
1184		}
1185	}
1186
1187	return false
1188}
1189
1190// handleMissingDexBootFile will either log a warning or create an error rule to create the fake
1191// file depending on the value returned from deferReportingMissingBootDexJar.
1192func handleMissingDexBootFile(ctx android.ModuleContext, module android.Module, fake android.WritablePath) {
1193	if deferReportingMissingBootDexJar(ctx, module) {
1194		// Create an error rule that pretends to create the output file but will actually fail if it
1195		// is run.
1196		ctx.Build(pctx, android.BuildParams{
1197			Rule:   android.ErrorRule,
1198			Output: fake,
1199			Args: map[string]string{
1200				"error": fmt.Sprintf("missing dependencies: boot dex jar for %s", module),
1201			},
1202		})
1203	} else {
1204		ctx.ModuleErrorf("module %s does not provide a dex jar", module)
1205	}
1206}
1207
1208// retrieveEncodedBootDexJarFromModule returns a path to the boot dex jar from the supplied module's
1209// DexJarBuildPath() method.
1210//
1211// The returned path will usually be to a dex jar file that has been encoded with hidden API flags.
1212// However, under certain conditions, e.g. errors, or special build configurations it will return
1213// a path to a fake file.
1214func retrieveEncodedBootDexJarFromModule(ctx android.ModuleContext, module android.Module) android.Path {
1215	bootDexJar := module.(interface{ DexJarBuildPath() android.Path }).DexJarBuildPath()
1216	if bootDexJar == nil {
1217		fake := android.PathForModuleOut(ctx, fmt.Sprintf("fake/encoded-dex/%s.jar", module.Name()))
1218		bootDexJar = fake
1219
1220		handleMissingDexBootFile(ctx, module, fake)
1221	}
1222	return bootDexJar
1223}
1224
1225// extractEncodedDexJarsFromModules extracts the encoded dex jars from the supplied modules.
1226func extractEncodedDexJarsFromModules(ctx android.ModuleContext, contents []android.Module) bootDexJarByModule {
1227	encodedDexJarsByModuleName := bootDexJarByModule{}
1228	for _, module := range contents {
1229		path := retrieveEncodedBootDexJarFromModule(ctx, module)
1230		encodedDexJarsByModuleName.addPath(module, path)
1231	}
1232	return encodedDexJarsByModuleName
1233}
1234