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