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