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 := module.(SdkLibraryDependency); ok {
302		if ctx.Config().ReleaseHiddenApiExportableStubs() {
303			dexJar = sdkLibrary.SdkApiExportableStubDexJar(ctx, kind)
304		} else {
305			dexJar = sdkLibrary.SdkApiStubDexJar(ctx, kind)
306		}
307
308	} else if j, ok := module.(UsesLibraryDependency); ok {
309		dexJar = j.DexJarBuildPath(ctx)
310	} else {
311		ctx.ModuleErrorf("dependency %s of module type %s does not support providing a dex jar", module, ctx.OtherModuleType(module))
312		return nil
313	}
314
315	if !dexJar.Valid() {
316		ctx.ModuleErrorf("dependency %s does not provide a dex jar: %s", module, dexJar.InvalidReason())
317		return nil
318	}
319	return dexJar.Path()
320}
321
322// HIDDENAPI_STUB_FLAGS_IMPL_FLAGS is the set of flags that identify implementation only signatures,
323// i.e. those signatures that are not part of any API (including the hidden API).
324var HIDDENAPI_STUB_FLAGS_IMPL_FLAGS = []string{}
325
326var HIDDENAPI_FLAGS_CSV_IMPL_FLAGS = []string{"blocked"}
327
328// buildRuleToGenerateHiddenAPIStubFlagsFile creates a rule to create a hidden API stub flags file.
329//
330// The rule is initialized but not built so that the caller can modify it and select an appropriate
331// name.
332func buildRuleToGenerateHiddenAPIStubFlagsFile(ctx android.BuilderContext, name, desc string, outputPath android.WritablePath, bootDexJars android.Paths, input HiddenAPIFlagInput, stubFlagSubsets SignatureCsvSubsets) {
333	// Singleton rule which applies hiddenapi on all boot class path dex files.
334	rule := android.NewRuleBuilder(pctx, ctx)
335
336	tempPath := tempPathForRestat(ctx, outputPath)
337
338	// Find the widest API stubs provided by the fragments on which this depends, if any.
339	dependencyStubDexJars := input.DependencyStubDexJarsByScope.StubDexJarsForWidestAPIScope()
340
341	// Add widest API stubs from the additional dependencies of this, if any.
342	dependencyStubDexJars = append(dependencyStubDexJars, input.AdditionalStubDexJarsByScope.StubDexJarsForWidestAPIScope()...)
343
344	command := rule.Command().
345		Tool(ctx.Config().HostToolPath(ctx, "hiddenapi")).
346		Text("list").
347		FlagForEachInput("--dependency-stub-dex=", dependencyStubDexJars).
348		FlagForEachInput("--boot-dex=", bootDexJars)
349
350	// If no module stub flags paths are provided then this must be being called for a
351	// bootclasspath_fragment and not the whole platform_bootclasspath.
352	if stubFlagSubsets == nil {
353		// This is being run on a fragment of the bootclasspath.
354		command.Flag("--fragment")
355	}
356
357	// Iterate over the api scopes in a fixed order.
358	for _, apiScope := range hiddenAPIFlagScopes {
359		// Merge in the stub dex jar paths for this api scope from the fragments on which it depends.
360		// They will be needed to resolve dependencies from this fragment's stubs to classes in the
361		// other fragment's APIs.
362		var paths android.Paths
363		paths = append(paths, input.DependencyStubDexJarsByScope.StubDexJarsForScope(apiScope)...)
364		paths = append(paths, input.AdditionalStubDexJarsByScope.StubDexJarsForScope(apiScope)...)
365		paths = append(paths, input.StubDexJarsByScope.StubDexJarsForScope(apiScope)...)
366		if len(paths) > 0 {
367			option := apiScope.hiddenAPIListOption
368			command.FlagWithInputList(option+"=", paths, ":")
369		}
370	}
371
372	// Add the output path.
373	command.FlagWithOutput("--out-api-flags=", tempPath)
374
375	// If there are stub flag files that have been generated by fragments on which this depends then
376	// use them to validate the stub flag file generated by the rules created by this method.
377	if !ctx.Config().DisableVerifyOverlaps() && len(stubFlagSubsets) > 0 {
378		validFile := buildRuleValidateOverlappingCsvFiles(ctx, name, desc, outputPath, stubFlagSubsets,
379			HIDDENAPI_STUB_FLAGS_IMPL_FLAGS)
380
381		// Add the file that indicates that the file generated by this is valid.
382		//
383		// This will cause the validation rule above to be run any time that the output of this rule
384		// changes but the validation will run in parallel with other rules that depend on this file.
385		command.Validation(validFile)
386	}
387
388	commitChangeForRestat(rule, tempPath, outputPath)
389
390	rule.Build(name, desc)
391}
392
393// HiddenAPIFlagFileProperties contains paths to the flag files that can be used to augment the
394// information obtained from annotations within the source code in order to create the complete set
395// of flags that should be applied to the dex implementation jars on the bootclasspath.
396//
397// Each property contains a list of paths. With the exception of the Unsupported_packages the paths
398// of each property reference a plain text file that contains a java signature per line. The flags
399// for each of those signatures will be updated in a property specific way.
400//
401// The Unsupported_packages property contains a list of paths, each of which is a plain text file
402// with one Java package per line. All members of all classes within that package (but not nested
403// packages) will be updated in a property specific way.
404type HiddenAPIFlagFileProperties struct {
405	Hidden_api struct {
406		// Marks each signature in the referenced files as being unsupported.
407		Unsupported []string `android:"path"`
408
409		// Marks each signature in the referenced files as being unsupported because it has been
410		// removed. Any conflicts with other flags are ignored.
411		Removed []string `android:"path"`
412
413		// Marks each signature in the referenced files as being supported only for
414		// targetSdkVersion <= R and low priority.
415		Max_target_r_low_priority []string `android:"path"`
416
417		// Marks each signature in the referenced files as being supported only for
418		// targetSdkVersion <= Q.
419		Max_target_q []string `android:"path"`
420
421		// Marks each signature in the referenced files as being supported only for
422		// targetSdkVersion <= P.
423		Max_target_p []string `android:"path"`
424
425		// Marks each signature in the referenced files as being supported only for
426		// targetSdkVersion <= O
427		// and low priority. Any conflicts with other flags are ignored.
428		Max_target_o_low_priority []string `android:"path"`
429
430		// Marks each signature in the referenced files as being blocked.
431		Blocked []string `android:"path"`
432
433		// Marks each signature in every package in the referenced files as being unsupported.
434		Unsupported_packages []string `android:"path"`
435	}
436}
437
438type hiddenAPIFlagFileCategory int
439
440const (
441	// The flag file category for removed members of the API.
442	//
443	// This is extracted from HiddenAPIFlagFileCategories as it is needed to add the dex signatures
444	// list of removed API members that are generated automatically from the removed.txt files provided
445	// by API stubs.
446	hiddenAPIFlagFileCategoryRemoved hiddenAPIFlagFileCategory = iota
447	hiddenAPIFlagFileCategoryUnsupported
448	hiddenAPIFlagFileCategoryMaxTargetRLowPriority
449	hiddenAPIFlagFileCategoryMaxTargetQ
450	hiddenAPIFlagFileCategoryMaxTargetP
451	hiddenAPIFlagFileCategoryMaxTargetOLowPriority
452	hiddenAPIFlagFileCategoryBlocked
453	hiddenAPIFlagFileCategoryUnsupportedPackages
454)
455
456func (c hiddenAPIFlagFileCategory) PropertyName() string {
457	switch c {
458	case hiddenAPIFlagFileCategoryRemoved:
459		return "removed"
460	case hiddenAPIFlagFileCategoryUnsupported:
461		return "unsupported"
462	case hiddenAPIFlagFileCategoryMaxTargetRLowPriority:
463		return "max_target_r_low_priority"
464	case hiddenAPIFlagFileCategoryMaxTargetQ:
465		return "max_target_q"
466	case hiddenAPIFlagFileCategoryMaxTargetP:
467		return "max_target_p"
468	case hiddenAPIFlagFileCategoryMaxTargetOLowPriority:
469		return "max_target_o_low_priority"
470	case hiddenAPIFlagFileCategoryBlocked:
471		return "blocked"
472	case hiddenAPIFlagFileCategoryUnsupportedPackages:
473		return "unsupported_packages"
474	default:
475		panic(fmt.Sprintf("Unknown hidden api flag file category type: %d", c))
476	}
477}
478
479// propertyValueReader retrieves the value of the property for this category from the set of properties.
480func (c hiddenAPIFlagFileCategory) propertyValueReader(properties *HiddenAPIFlagFileProperties) []string {
481	switch c {
482	case hiddenAPIFlagFileCategoryRemoved:
483		return properties.Hidden_api.Removed
484	case hiddenAPIFlagFileCategoryUnsupported:
485		return properties.Hidden_api.Unsupported
486	case hiddenAPIFlagFileCategoryMaxTargetRLowPriority:
487		return properties.Hidden_api.Max_target_r_low_priority
488	case hiddenAPIFlagFileCategoryMaxTargetQ:
489		return properties.Hidden_api.Max_target_q
490	case hiddenAPIFlagFileCategoryMaxTargetP:
491		return properties.Hidden_api.Max_target_p
492	case hiddenAPIFlagFileCategoryMaxTargetOLowPriority:
493		return properties.Hidden_api.Max_target_o_low_priority
494	case hiddenAPIFlagFileCategoryBlocked:
495		return properties.Hidden_api.Blocked
496	case hiddenAPIFlagFileCategoryUnsupportedPackages:
497		return properties.Hidden_api.Unsupported_packages
498	default:
499		panic(fmt.Sprintf("Unknown hidden api flag file category type: %d", c))
500	}
501}
502
503// commandMutator adds the appropriate command line options for this category to the supplied command
504func (c hiddenAPIFlagFileCategory) commandMutator(command *android.RuleBuilderCommand, path android.Path) {
505	switch c {
506	case hiddenAPIFlagFileCategoryRemoved:
507		command.FlagWithInput("--unsupported ", path).Flag("--ignore-conflicts ").FlagWithArg("--tag ", "removed")
508	case hiddenAPIFlagFileCategoryUnsupported:
509		command.FlagWithInput("--unsupported ", path)
510	case hiddenAPIFlagFileCategoryMaxTargetRLowPriority:
511		command.FlagWithInput("--max-target-r ", path).FlagWithArg("--tag ", "lo-prio")
512	case hiddenAPIFlagFileCategoryMaxTargetQ:
513		command.FlagWithInput("--max-target-q ", path)
514	case hiddenAPIFlagFileCategoryMaxTargetP:
515		command.FlagWithInput("--max-target-p ", path)
516	case hiddenAPIFlagFileCategoryMaxTargetOLowPriority:
517		command.FlagWithInput("--max-target-o ", path).Flag("--ignore-conflicts ").FlagWithArg("--tag ", "lo-prio")
518	case hiddenAPIFlagFileCategoryBlocked:
519		command.FlagWithInput("--blocked ", path)
520	case hiddenAPIFlagFileCategoryUnsupportedPackages:
521		command.FlagWithInput("--unsupported ", path).Flag("--packages ")
522	default:
523		panic(fmt.Sprintf("Unknown hidden api flag file category type: %d", c))
524	}
525}
526
527type hiddenAPIFlagFileCategories []hiddenAPIFlagFileCategory
528
529var HiddenAPIFlagFileCategories = hiddenAPIFlagFileCategories{
530	// See HiddenAPIFlagFileProperties.Unsupported
531	hiddenAPIFlagFileCategoryUnsupported,
532	// See HiddenAPIFlagFileProperties.Removed
533	hiddenAPIFlagFileCategoryRemoved,
534	// See HiddenAPIFlagFileProperties.Max_target_r_low_priority
535	hiddenAPIFlagFileCategoryMaxTargetRLowPriority,
536	// See HiddenAPIFlagFileProperties.Max_target_q
537	hiddenAPIFlagFileCategoryMaxTargetQ,
538	// See HiddenAPIFlagFileProperties.Max_target_p
539	hiddenAPIFlagFileCategoryMaxTargetP,
540	// See HiddenAPIFlagFileProperties.Max_target_o_low_priority
541	hiddenAPIFlagFileCategoryMaxTargetOLowPriority,
542	// See HiddenAPIFlagFileProperties.Blocked
543	hiddenAPIFlagFileCategoryBlocked,
544	// See HiddenAPIFlagFileProperties.Unsupported_packages
545	hiddenAPIFlagFileCategoryUnsupportedPackages,
546}
547
548// FlagFilesByCategory maps a hiddenAPIFlagFileCategory to the paths to the files in that category.
549type FlagFilesByCategory map[hiddenAPIFlagFileCategory]android.Paths
550
551// append the supplied flags files to the corresponding category in this map.
552func (s FlagFilesByCategory) append(other FlagFilesByCategory) {
553	for _, category := range HiddenAPIFlagFileCategories {
554		s[category] = append(s[category], other[category]...)
555	}
556}
557
558// sort the paths for each category in this map.
559func (s FlagFilesByCategory) sort() {
560	for category, value := range s {
561		s[category] = android.SortedUniquePaths(value)
562	}
563}
564
565// HiddenAPIInfo contains information provided by the hidden API processing.
566//
567// That includes paths resolved from HiddenAPIFlagFileProperties and also generated by hidden API
568// processing.
569type HiddenAPIInfo struct {
570	// FlagFilesByCategory maps from the flag file category to the paths containing information for
571	// that category.
572	FlagFilesByCategory FlagFilesByCategory
573
574	// The paths to the stub dex jars for each of the *HiddenAPIScope in hiddenAPIScopes provided by
575	// this fragment and the fragments on which this depends.
576	TransitiveStubDexJarsByScope StubDexJarsByModule
577
578	// The output from the hidden API processing needs to be made available to other modules.
579	HiddenAPIFlagOutput
580}
581
582func newHiddenAPIInfo() *HiddenAPIInfo {
583	info := HiddenAPIInfo{
584		FlagFilesByCategory:          FlagFilesByCategory{},
585		TransitiveStubDexJarsByScope: StubDexJarsByModule{},
586	}
587	return &info
588}
589
590func (i *HiddenAPIInfo) mergeFromFragmentDeps(ctx android.ModuleContext, fragments []android.Module) {
591	// Merge all the information from the fragments. The fragments form a DAG so it is possible that
592	// this will introduce duplicates so they will be resolved after processing all the fragments.
593	for _, fragment := range fragments {
594		if info, ok := android.OtherModuleProvider(ctx, fragment, HiddenAPIInfoProvider); ok {
595			i.TransitiveStubDexJarsByScope.addStubDexJarsByModule(info.TransitiveStubDexJarsByScope)
596		}
597	}
598}
599
600// StubFlagSubset returns a SignatureCsvSubset that contains a path to a filtered-stub-flags.csv
601// file and a path to a signature-patterns.csv file that defines a subset of the monolithic stub
602// flags file, i.e. out/soong/hiddenapi/hiddenapi-stub-flags.txt, against which it will be compared.
603func (i *HiddenAPIInfo) StubFlagSubset() SignatureCsvSubset {
604	return SignatureCsvSubset{i.FilteredStubFlagsPath, i.SignaturePatternsPath}
605}
606
607// FlagSubset returns a SignatureCsvSubset that contains a path to a filtered-flags.csv file and a
608// path to a signature-patterns.csv file that defines a subset of the monolithic flags file, i.e.
609// out/soong/hiddenapi/hiddenapi-flags.csv, against which it will be compared.
610func (i *HiddenAPIInfo) FlagSubset() SignatureCsvSubset {
611	return SignatureCsvSubset{i.FilteredFlagsPath, i.SignaturePatternsPath}
612}
613
614var HiddenAPIInfoProvider = blueprint.NewProvider[HiddenAPIInfo]()
615
616// HiddenAPIInfoForSdk contains information provided by the hidden API processing for use
617// by the sdk snapshot.
618//
619// That includes paths resolved from HiddenAPIFlagFileProperties and also generated by hidden API
620// processing.
621type HiddenAPIInfoForSdk struct {
622	// FlagFilesByCategory maps from the flag file category to the paths containing information for
623	// that category.
624	FlagFilesByCategory FlagFilesByCategory
625
626	// The output from the hidden API processing needs to be made available to other modules.
627	HiddenAPIFlagOutput
628}
629
630// Provides hidden API info for the sdk snapshot.
631var HiddenAPIInfoForSdkProvider = blueprint.NewProvider[HiddenAPIInfoForSdk]()
632
633// ModuleStubDexJars contains the stub dex jars provided by a single module.
634//
635// It maps a *HiddenAPIScope to the path to stub dex jars appropriate for that scope. See
636// hiddenAPIScopes for a list of the acceptable *HiddenAPIScope values.
637type ModuleStubDexJars map[*HiddenAPIScope]android.Path
638
639// stubDexJarForWidestAPIScope returns the stub dex jars for the widest API scope provided by this
640// map.
641//
642// The relative width of APIs is determined by their order in hiddenAPIScopes.
643func (s ModuleStubDexJars) stubDexJarForWidestAPIScope() android.Path {
644	for i := len(hiddenAPIScopes) - 1; i >= 0; i-- {
645		apiScope := hiddenAPIScopes[i]
646		if stubsForAPIScope, ok := s[apiScope]; ok {
647			return stubsForAPIScope
648		}
649	}
650
651	return nil
652}
653
654// StubDexJarsByModule contains the stub dex jars provided by a set of modules.
655//
656// It maps a module name to the path to the stub dex jars provided by that module.
657type StubDexJarsByModule map[string]ModuleStubDexJars
658
659// addStubDexJar adds a stub dex jar path provided by the specified module for the specified scope.
660func (s StubDexJarsByModule) addStubDexJar(ctx android.ModuleContext, module android.Module, scope *HiddenAPIScope, stubDexJar android.Path) {
661	name := android.RemoveOptionalPrebuiltPrefix(module.Name())
662
663	// Each named module provides one dex jar for each scope. However, in some cases different API
664	// versions of a single classes are provided by separate modules. e.g. the core platform
665	// version of java.lang.Object is provided by the legacy.art.module.platform.api module but the
666	// public version is provided by the art.module.public.api module. In those cases it is necessary
667	// to treat all those modules as they were the same name, otherwise it will result in multiple
668	// definitions of a single class being passed to hidden API processing which will cause an error.
669	if name == scope.nonUpdatablePrebuiltModule || name == scope.nonUpdatableSourceModule || name == scope.nonUpdatableFromTextModule {
670		// Treat all *android-non-updatable* modules as if they were part of an android-non-updatable
671		// java_sdk_library.
672		// TODO(b/192067200): Remove once android-non-updatable is a java_sdk_library or equivalent.
673		name = "android-non-updatable"
674	} else if name == "legacy.art.module.platform.api" {
675		// Treat legacy.art.module.platform.api as if it was an API scope provided by the
676		// art.module.public.api java_sdk_library which will be the case once the former has been
677		// migrated to a module_lib API.
678		name = "art.module.public.api"
679	} else if name == "legacy.i18n.module.platform.api" {
680		// Treat legacy.i18n.module.platform.api as if it was an API scope provided by the
681		// i18n.module.public.api java_sdk_library which will be the case once the former has been
682		// migrated to a module_lib API.
683		name = "i18n.module.public.api"
684	} else if name == "conscrypt.module.platform.api" {
685		// Treat conscrypt.module.platform.api as if it was an API scope provided by the
686		// conscrypt.module.public.api java_sdk_library which will be the case once the former has been
687		// migrated to a module_lib API.
688		name = "conscrypt.module.public.api"
689	} else if d, ok := module.(SdkLibraryComponentDependency); ok {
690		sdkLibraryName := d.SdkLibraryName()
691		if sdkLibraryName != nil {
692			// The module is a component of a java_sdk_library so use the name of the java_sdk_library.
693			// e.g. if this module is `foo.system.stubs` and is part of the `foo` java_sdk_library then
694			// use `foo` as the name.
695			name = *sdkLibraryName
696		}
697	}
698	stubDexJarsByScope := s[name]
699	if stubDexJarsByScope == nil {
700		stubDexJarsByScope = ModuleStubDexJars{}
701		s[name] = stubDexJarsByScope
702	}
703	stubDexJarsByScope[scope] = stubDexJar
704}
705
706// addStubDexJarsByModule adds the stub dex jars in the supplied StubDexJarsByModule to this map.
707func (s StubDexJarsByModule) addStubDexJarsByModule(other StubDexJarsByModule) {
708	for module, stubDexJarsByScope := range other {
709		s[module] = stubDexJarsByScope
710	}
711}
712
713// StubDexJarsForWidestAPIScope returns a list of stub dex jars containing the widest API scope
714// provided by each module.
715//
716// The relative width of APIs is determined by their order in hiddenAPIScopes.
717func (s StubDexJarsByModule) StubDexJarsForWidestAPIScope() android.Paths {
718	stubDexJars := android.Paths{}
719	modules := android.SortedKeys(s)
720	for _, module := range modules {
721		stubDexJarsByScope := s[module]
722
723		stubDexJars = append(stubDexJars, stubDexJarsByScope.stubDexJarForWidestAPIScope())
724	}
725
726	return stubDexJars
727}
728
729// StubDexJarsForScope returns a list of stub dex jars containing the stub dex jars provided by each
730// module for the specified scope.
731//
732// If a module does not provide a stub dex jar for the supplied scope then it does not contribute to
733// the returned list.
734func (s StubDexJarsByModule) StubDexJarsForScope(scope *HiddenAPIScope) android.Paths {
735	stubDexJars := android.Paths{}
736	modules := android.SortedKeys(s)
737	for _, module := range modules {
738		stubDexJarsByScope := s[module]
739		// Not every module will have the same set of
740		if jars, ok := stubDexJarsByScope[scope]; ok {
741			stubDexJars = append(stubDexJars, jars)
742		}
743	}
744
745	return stubDexJars
746}
747
748type HiddenAPIPropertyInfo struct {
749	// FlagFilesByCategory contains the flag files that override the initial flags that are derived
750	// from the stub dex files.
751	FlagFilesByCategory FlagFilesByCategory
752
753	// See HiddenAPIFlagFileProperties.Package_prefixes
754	PackagePrefixes []string
755
756	// See HiddenAPIFlagFileProperties.Single_packages
757	SinglePackages []string
758
759	// See HiddenAPIFlagFileProperties.Split_packages
760	SplitPackages []string
761}
762
763var hiddenAPIPropertyInfoProvider = blueprint.NewProvider[HiddenAPIPropertyInfo]()
764
765// newHiddenAPIPropertyInfo creates a new initialized HiddenAPIPropertyInfo struct.
766func newHiddenAPIPropertyInfo() HiddenAPIPropertyInfo {
767	return HiddenAPIPropertyInfo{
768		FlagFilesByCategory: FlagFilesByCategory{},
769	}
770}
771
772// extractFlagFilesFromProperties extracts the paths to flag files that are specified in the
773// supplied properties and stores them in this struct.
774func (i *HiddenAPIPropertyInfo) extractFlagFilesFromProperties(ctx android.ModuleContext, p *HiddenAPIFlagFileProperties) {
775	for _, category := range HiddenAPIFlagFileCategories {
776		paths := android.PathsForModuleSrc(ctx, category.propertyValueReader(p))
777		i.FlagFilesByCategory[category] = paths
778	}
779}
780
781// extractPackageRulesFromProperties extracts the package rules that are specified in the supplied
782// properties and stores them in this struct.
783func (i *HiddenAPIPropertyInfo) extractPackageRulesFromProperties(p *HiddenAPIPackageProperties) {
784	i.PackagePrefixes = p.Hidden_api.Package_prefixes
785	i.SinglePackages = p.Hidden_api.Single_packages
786	i.SplitPackages = p.Hidden_api.Split_packages
787}
788
789func (i *HiddenAPIPropertyInfo) gatherPropertyInfo(ctx android.ModuleContext, contents []android.Module) {
790	for _, module := range contents {
791		if info, ok := android.OtherModuleProvider(ctx, module, hiddenAPIPropertyInfoProvider); ok {
792			i.FlagFilesByCategory.append(info.FlagFilesByCategory)
793			i.PackagePrefixes = append(i.PackagePrefixes, info.PackagePrefixes...)
794			i.SinglePackages = append(i.SinglePackages, info.SinglePackages...)
795			i.SplitPackages = append(i.SplitPackages, info.SplitPackages...)
796		}
797	}
798
799	// Dedup and sort the information to ensure consistent builds.
800	i.FlagFilesByCategory.sort()
801	i.PackagePrefixes = android.SortedUniqueStrings(i.PackagePrefixes)
802	i.SinglePackages = android.SortedUniqueStrings(i.SinglePackages)
803	i.SplitPackages = android.SortedUniqueStrings(i.SplitPackages)
804}
805
806// HiddenAPIFlagInput encapsulates information obtained from a module and its dependencies that are
807// needed for hidden API flag generation.
808type HiddenAPIFlagInput struct {
809	HiddenAPIPropertyInfo
810
811	// StubDexJarsByScope contains the stub dex jars for different *HiddenAPIScope and which determine
812	// the initial flags for each dex member.
813	StubDexJarsByScope StubDexJarsByModule
814
815	// DependencyStubDexJarsByScope contains the stub dex jars provided by the fragments on which this
816	// depends. It is the result of merging HiddenAPIInfo.TransitiveStubDexJarsByScope from each
817	// fragment on which this depends.
818	DependencyStubDexJarsByScope StubDexJarsByModule
819
820	// AdditionalStubDexJarsByScope contains stub dex jars provided by other modules in addition to
821	// the ones that are obtained from fragments on which this depends.
822	//
823	// These are kept separate from stub dex jars in HiddenAPIFlagInput.DependencyStubDexJarsByScope
824	// as there are not propagated transitively to other fragments that depend on this.
825	AdditionalStubDexJarsByScope StubDexJarsByModule
826
827	// RemovedTxtFiles is the list of removed.txt files provided by java_sdk_library modules that are
828	// specified in the bootclasspath_fragment's stub_libs and contents properties.
829	RemovedTxtFiles android.Paths
830}
831
832// newHiddenAPIFlagInput creates a new initialized HiddenAPIFlagInput struct.
833func newHiddenAPIFlagInput() HiddenAPIFlagInput {
834	input := HiddenAPIFlagInput{
835		HiddenAPIPropertyInfo:        newHiddenAPIPropertyInfo(),
836		StubDexJarsByScope:           StubDexJarsByModule{},
837		DependencyStubDexJarsByScope: StubDexJarsByModule{},
838		AdditionalStubDexJarsByScope: StubDexJarsByModule{},
839	}
840
841	return input
842}
843
844// gatherStubLibInfo gathers information from the stub libs needed by hidden API processing from the
845// dependencies added in hiddenAPIAddStubLibDependencies.
846//
847// That includes paths to the stub dex jars as well as paths to the *removed.txt files.
848func (i *HiddenAPIFlagInput) gatherStubLibInfo(ctx android.ModuleContext, contents []android.Module) {
849	addFromModule := func(ctx android.ModuleContext, module android.Module, apiScope *HiddenAPIScope) {
850		sdkKind := apiScope.sdkKind
851		dexJar := hiddenAPIRetrieveDexJarBuildPath(ctx, module, sdkKind)
852		if dexJar != nil {
853			i.StubDexJarsByScope.addStubDexJar(ctx, module, apiScope, dexJar)
854		}
855
856		if sdkLibrary, ok := module.(SdkLibraryDependency); ok {
857			removedTxtFile := sdkLibrary.SdkRemovedTxtFile(ctx, sdkKind)
858			i.RemovedTxtFiles = append(i.RemovedTxtFiles, removedTxtFile.AsPaths()...)
859		}
860	}
861
862	// If the contents includes any java_sdk_library modules then add them to the stubs.
863	for _, module := range contents {
864		if _, ok := module.(SdkLibraryDependency); ok {
865			// Add information for every possible API scope needed by hidden API.
866			for _, apiScope := range hiddenAPISdkLibrarySupportedScopes {
867				addFromModule(ctx, module, apiScope)
868			}
869		}
870	}
871
872	ctx.VisitDirectDeps(func(module android.Module) {
873		tag := ctx.OtherModuleDependencyTag(module)
874		if hiddenAPIStubsTag, ok := tag.(hiddenAPIStubsDependencyTag); ok {
875			apiScope := hiddenAPIStubsTag.apiScope
876			if hiddenAPIStubsTag.fromAdditionalDependency {
877				dexJar := hiddenAPIRetrieveDexJarBuildPath(ctx, module, apiScope.sdkKind)
878				if dexJar != nil {
879					i.AdditionalStubDexJarsByScope.addStubDexJar(ctx, module, apiScope, dexJar)
880				}
881			} else {
882				addFromModule(ctx, module, apiScope)
883			}
884		}
885	})
886
887	// Normalize the paths, i.e. remove duplicates and sort.
888	i.RemovedTxtFiles = android.SortedUniquePaths(i.RemovedTxtFiles)
889}
890
891func (i *HiddenAPIFlagInput) transitiveStubDexJarsByScope() StubDexJarsByModule {
892	transitive := i.DependencyStubDexJarsByScope
893	transitive.addStubDexJarsByModule(i.StubDexJarsByScope)
894	return transitive
895}
896
897// HiddenAPIFlagOutput contains paths to output files from the hidden API flag generation for a
898// bootclasspath_fragment module.
899type HiddenAPIFlagOutput struct {
900	// The path to the generated annotation-flags.csv file.
901	AnnotationFlagsPath android.Path
902
903	// The path to the generated metadata.csv file.
904	MetadataPath android.Path
905
906	// The path to the generated index.csv file.
907	IndexPath android.Path
908
909	// The path to the generated stub-flags.csv file.
910	StubFlagsPath android.Path
911
912	// The path to the generated all-flags.csv file.
913	AllFlagsPath android.Path
914
915	// The path to the generated signature-patterns.txt file which defines the subset of the
916	// monolithic hidden API files provided in this.
917	SignaturePatternsPath android.Path
918
919	// The path to the generated filtered-stub-flags.csv file.
920	FilteredStubFlagsPath android.Path
921
922	// The path to the generated filtered-flags.csv file.
923	FilteredFlagsPath android.Path
924}
925
926// bootDexJarByModule is a map from base module name (without prebuilt_ prefix) to the boot dex
927// path.
928type bootDexJarByModule map[string]android.Path
929
930// addPath adds the path for a module to the map.
931func (b bootDexJarByModule) addPath(module android.Module, path android.Path) {
932	b[android.RemoveOptionalPrebuiltPrefix(module.Name())] = path
933}
934
935// bootDexJars returns the boot dex jar paths sorted by their keys.
936func (b bootDexJarByModule) bootDexJars() android.Paths {
937	paths := android.Paths{}
938	for _, k := range android.SortedKeys(b) {
939		paths = append(paths, b[k])
940	}
941	return paths
942}
943
944// bootDexJarsWithoutCoverage returns the boot dex jar paths sorted by their keys without coverage
945// libraries if present.
946func (b bootDexJarByModule) bootDexJarsWithoutCoverage() android.Paths {
947	paths := android.Paths{}
948	for _, k := range android.SortedKeys(b) {
949		if k == "jacocoagent" {
950			continue
951		}
952		paths = append(paths, b[k])
953	}
954	return paths
955}
956
957// HiddenAPIOutput encapsulates the output from the hidden API processing.
958type HiddenAPIOutput struct {
959	HiddenAPIFlagOutput
960
961	// The map from base module name to the path to the encoded boot dex file.
962	// This field is not available in prebuilt apexes
963	EncodedBootDexFilesByModule bootDexJarByModule
964}
965
966// pathForValidation creates a path of the same type as the supplied type but with a name of
967// <path>.valid.
968//
969// e.g. If path is an OutputPath for out/soong/hiddenapi/hiddenapi-flags.csv then this will return
970// an OutputPath for out/soong/hiddenapi/hiddenapi-flags.csv.valid
971func pathForValidation(ctx android.PathContext, path android.WritablePath) android.WritablePath {
972	extWithoutLeadingDot := strings.TrimPrefix(path.Ext(), ".")
973	return path.ReplaceExtension(ctx, extWithoutLeadingDot+".valid")
974}
975
976// buildRuleToGenerateHiddenApiFlags creates a rule to create the monolithic hidden API flags from
977// the flags from all the modules, the stub flags, augmented with some additional configuration
978// files.
979//
980// baseFlagsPath is the path to the flags file containing all the information from the stubs plus
981// an entry for every single member in the dex implementation jars of the individual modules. Every
982// signature in any of the other files MUST be included in this file.
983//
984// annotationFlags is the path to the annotation flags file generated from annotation information
985// in each module.
986//
987// hiddenAPIInfo is a struct containing paths to files that augment the information provided by
988// the annotationFlags.
989func buildRuleToGenerateHiddenApiFlags(ctx android.BuilderContext, name, desc string,
990	outputPath android.WritablePath, baseFlagsPath android.Path, annotationFlagPaths android.Paths,
991	flagFilesByCategory FlagFilesByCategory, flagSubsets SignatureCsvSubsets, generatedRemovedDexSignatures android.OptionalPath) {
992
993	// Create the rule that will generate the flag files.
994	tempPath := tempPathForRestat(ctx, outputPath)
995	rule := android.NewRuleBuilder(pctx, ctx)
996	command := rule.Command().
997		BuiltTool("generate_hiddenapi_lists").
998		FlagWithInput("--csv ", baseFlagsPath).
999		Inputs(annotationFlagPaths).
1000		FlagWithOutput("--output ", tempPath)
1001
1002	// Add the options for the different categories of flag files.
1003	for _, category := range HiddenAPIFlagFileCategories {
1004		paths := flagFilesByCategory[category]
1005		for _, path := range paths {
1006			category.commandMutator(command, path)
1007		}
1008	}
1009
1010	// If available then pass the automatically generated file containing dex signatures of removed
1011	// API members to the rule so they can be marked as removed.
1012	if generatedRemovedDexSignatures.Valid() {
1013		hiddenAPIFlagFileCategoryRemoved.commandMutator(command, generatedRemovedDexSignatures.Path())
1014	}
1015
1016	commitChangeForRestat(rule, tempPath, outputPath)
1017
1018	// If there are flag files that have been generated by fragments on which this depends then use
1019	// them to validate the flag file generated by the rules created by this method.
1020	if !ctx.Config().DisableVerifyOverlaps() && len(flagSubsets) > 0 {
1021		validFile := buildRuleValidateOverlappingCsvFiles(ctx, name, desc, outputPath, flagSubsets,
1022			HIDDENAPI_FLAGS_CSV_IMPL_FLAGS)
1023
1024		// Add the file that indicates that the file generated by this is valid.
1025		//
1026		// This will cause the validation rule above to be run any time that the output of this rule
1027		// changes but the validation will run in parallel with other rules that depend on this file.
1028		command.Validation(validFile)
1029	}
1030
1031	rule.Build(name, desc)
1032}
1033
1034// SignatureCsvSubset describes a subset of a monolithic flags file, i.e. either
1035// out/soong/hiddenapi/hiddenapi-stub-flags.txt or out/soong/hiddenapi/hiddenapi-flags.csv
1036type SignatureCsvSubset struct {
1037	// The path to the CSV file containing hidden API flags.
1038	//
1039	// It has the dex member signature as the first column, with flags, one per column, in the
1040	// subsequent columns.
1041	CsvFile android.Path
1042
1043	// The path to the CSV file containing the signature patterns.
1044	//
1045	// It is a single column CSV file with the column containing a signature pattern.
1046	SignaturePatternsFile android.Path
1047}
1048
1049type SignatureCsvSubsets []SignatureCsvSubset
1050
1051func (s SignatureCsvSubsets) RelativeToTop() []string {
1052	result := []string{}
1053	for _, subset := range s {
1054		result = append(result, fmt.Sprintf("%s:%s", subset.CsvFile.RelativeToTop(), subset.SignaturePatternsFile.RelativeToTop()))
1055	}
1056	return result
1057}
1058
1059// buildRuleSignaturePatternsFile creates a rule to generate a file containing the set of signature
1060// patterns that will select a subset of the monolithic flags.
1061func buildRuleSignaturePatternsFile(
1062	ctx android.ModuleContext, flagsPath android.Path,
1063	splitPackages []string, packagePrefixes []string, singlePackages []string,
1064	suffix string) android.Path {
1065	hiddenApiSubDir := "modular-hiddenapi" + suffix
1066
1067	patternsFile := android.PathForModuleOut(ctx, hiddenApiSubDir, "signature-patterns.csv")
1068	// Create a rule to validate the output from the following rule.
1069	rule := android.NewRuleBuilder(pctx, ctx)
1070
1071	// Quote any * in the packages to avoid them being expanded by the shell.
1072	quotedSplitPackages := []string{}
1073	for _, pkg := range splitPackages {
1074		quotedSplitPackages = append(quotedSplitPackages, strings.ReplaceAll(pkg, "*", "\\*"))
1075	}
1076
1077	rule.Command().
1078		BuiltTool("signature_patterns").
1079		FlagWithInput("--flags ", flagsPath).
1080		FlagForEachArg("--split-package ", quotedSplitPackages).
1081		FlagForEachArg("--package-prefix ", packagePrefixes).
1082		FlagForEachArg("--single-package ", singlePackages).
1083		FlagWithOutput("--output ", patternsFile)
1084	rule.Build("hiddenAPISignaturePatterns"+suffix, "hidden API signature patterns"+suffix)
1085
1086	return patternsFile
1087}
1088
1089// buildRuleRemoveSignaturesWithImplementationFlags creates a rule that will remove signatures from
1090// the input flags file which have only the implementation flags, i.e. are not part of an API.
1091//
1092// The implementationFlags specifies the set of default flags that identifies the signature of a
1093// private, implementation only, member. Signatures that match those flags are removed from the
1094// flags as they are implementation only.
1095//
1096// This is used to remove implementation only signatures from the signature files that are persisted
1097// in the sdk snapshot as the sdk snapshots should not include implementation details. The
1098// signatures generated by this method will be compared by the buildRuleValidateOverlappingCsvFiles
1099// method which treats any missing signatures as if they were implementation only signatures.
1100func buildRuleRemoveSignaturesWithImplementationFlags(ctx android.BuilderContext,
1101	name string, desc string, inputPath android.Path, filteredPath android.WritablePath,
1102	implementationFlags []string) {
1103
1104	rule := android.NewRuleBuilder(pctx, ctx)
1105	implementationFlagPattern := ""
1106	for _, implementationFlag := range implementationFlags {
1107		implementationFlagPattern = implementationFlagPattern + "," + implementationFlag
1108	}
1109	rule.Command().
1110		Text(`grep -vE "^[^,]+` + implementationFlagPattern + `$"`).Input(inputPath).
1111		Text(">").Output(filteredPath).
1112		// Grep's exit code depends on whether it finds anything. It is 0 (build success) when it finds
1113		// something and 1 (build failure) when it does not and 2 (when it encounters an error).
1114		// However, while it is unlikely it is not an error if this does not find any matches. The
1115		// following will only run if the grep does not find something and in that case it will treat
1116		// an exit code of 1 as success and anything else as failure.
1117		Text("|| test $? -eq 1")
1118	rule.Build(name, desc)
1119}
1120
1121// buildRuleValidateOverlappingCsvFiles checks that the modular CSV files, i.e. the files generated
1122// by the individual bootclasspath_fragment modules are subsets of the monolithic CSV file.
1123//
1124// The implementationFlags specifies the set of default flags that identifies the signature of a
1125// private, implementation only, member. A signature which is present in a monolithic flags subset
1126// defined by SignatureCsvSubset but which is not present in the flags file from the corresponding
1127// module is assumed to be an implementation only member and so must have these flags.
1128func buildRuleValidateOverlappingCsvFiles(ctx android.BuilderContext, name string, desc string,
1129	monolithicFilePath android.WritablePath, csvSubsets SignatureCsvSubsets,
1130	implementationFlags []string) android.WritablePath {
1131	// The file which is used to record that the flags file is valid.
1132	validFile := pathForValidation(ctx, monolithicFilePath)
1133
1134	// Create a rule to validate the output from the following rule.
1135	rule := android.NewRuleBuilder(pctx, ctx)
1136	command := rule.Command().
1137		BuiltTool("verify_overlaps").
1138		FlagWithInput("--monolithic-flags ", monolithicFilePath)
1139
1140	for _, subset := range csvSubsets {
1141		command.
1142			Flag("--module-flags ").
1143			Textf("%s:%s", subset.CsvFile, subset.SignaturePatternsFile).
1144			Implicit(subset.CsvFile).Implicit(subset.SignaturePatternsFile)
1145	}
1146
1147	for _, implementationFlag := range implementationFlags {
1148		command.FlagWithArg("--implementation-flag ", implementationFlag)
1149	}
1150
1151	// If validation passes then update the file that records that.
1152	command.Text("&& touch").Output(validFile)
1153	rule.Build(name+"Validation", desc+" validation")
1154
1155	return validFile
1156}
1157
1158// hiddenAPIFlagRulesForBootclasspathFragment will generate all the flags for a fragment of the
1159// bootclasspath.
1160//
1161// It takes:
1162// * Map from android.SdkKind to stub dex jar paths defining the API for that sdk kind.
1163// * The list of modules that are the contents of the fragment.
1164// * The additional manually curated flag files to use.
1165//
1166// It generates:
1167// * stub-flags.csv
1168// * annotation-flags.csv
1169// * metadata.csv
1170// * index.csv
1171// * all-flags.csv
1172func hiddenAPIFlagRulesForBootclasspathFragment(ctx android.ModuleContext, bootDexInfoByModule bootDexInfoByModule, contents []android.Module, input HiddenAPIFlagInput, suffix string) HiddenAPIFlagOutput {
1173	hiddenApiSubDir := "modular-hiddenapi" + suffix
1174
1175	// Generate the stub-flags.csv.
1176	stubFlagsCSV := android.PathForModuleOut(ctx, hiddenApiSubDir, "stub-flags.csv")
1177	buildRuleToGenerateHiddenAPIStubFlagsFile(ctx, "modularHiddenAPIStubFlagsFile"+suffix, "modular hiddenapi stub flags", stubFlagsCSV, bootDexInfoByModule.bootDexJars(), input, nil)
1178
1179	// Extract the classes jars from the contents.
1180	classesJars := extractClassesJarsFromModules(contents)
1181
1182	// Generate the set of flags from the annotations in the source code.
1183	annotationFlagsCSV := android.PathForModuleOut(ctx, hiddenApiSubDir, "annotation-flags.csv")
1184	buildRuleToGenerateAnnotationFlags(ctx, "modular hiddenapi annotation flags"+suffix, classesJars, stubFlagsCSV, annotationFlagsCSV)
1185
1186	// Generate the metadata from the annotations in the source code.
1187	metadataCSV := android.PathForModuleOut(ctx, hiddenApiSubDir, "metadata.csv")
1188	buildRuleToGenerateMetadata(ctx, "modular hiddenapi metadata"+suffix, classesJars, stubFlagsCSV, metadataCSV)
1189
1190	// Generate the index file from the CSV files in the classes jars.
1191	indexCSV := android.PathForModuleOut(ctx, hiddenApiSubDir, "index.csv")
1192	buildRuleToGenerateIndex(ctx, "modular hiddenapi index"+suffix, classesJars, indexCSV)
1193
1194	// Removed APIs need to be marked and in order to do that the hiddenAPIInfo needs to specify files
1195	// containing dex signatures of all the removed APIs. In the monolithic files that is done by
1196	// manually combining all the removed.txt files for each API and then converting them to dex
1197	// signatures, see the combined-removed-dex module. This does that automatically by using the
1198	// *removed.txt files retrieved from the java_sdk_library modules that are specified in the
1199	// stub_libs and contents properties of a bootclasspath_fragment.
1200	removedDexSignatures := buildRuleToGenerateRemovedDexSignatures(ctx, suffix, input.RemovedTxtFiles)
1201
1202	// Generate the all-flags.csv which are the flags that will, in future, be encoded into the dex
1203	// files.
1204	allFlagsCSV := android.PathForModuleOut(ctx, hiddenApiSubDir, "all-flags.csv")
1205	buildRuleToGenerateHiddenApiFlags(ctx, "modularHiddenApiAllFlags"+suffix, "modular hiddenapi all flags"+suffix, allFlagsCSV, stubFlagsCSV, android.Paths{annotationFlagsCSV}, input.FlagFilesByCategory, nil, removedDexSignatures)
1206
1207	// Generate the filtered-stub-flags.csv file which contains the filtered stub flags that will be
1208	// compared against the monolithic stub flags.
1209	filteredStubFlagsCSV := android.PathForModuleOut(ctx, hiddenApiSubDir, "filtered-stub-flags.csv")
1210	buildRuleRemoveSignaturesWithImplementationFlags(ctx, "modularHiddenApiFilteredStubFlags"+suffix,
1211		"modular hiddenapi filtered stub flags"+suffix, stubFlagsCSV, filteredStubFlagsCSV,
1212		HIDDENAPI_STUB_FLAGS_IMPL_FLAGS)
1213
1214	// Generate the filtered-flags.csv file which contains the filtered flags that will be compared
1215	// against the monolithic flags.
1216	filteredFlagsCSV := android.PathForModuleOut(ctx, hiddenApiSubDir, "filtered-flags.csv")
1217	buildRuleRemoveSignaturesWithImplementationFlags(ctx, "modularHiddenApiFilteredFlags"+suffix,
1218		"modular hiddenapi filtered flags"+suffix, allFlagsCSV, filteredFlagsCSV,
1219		HIDDENAPI_FLAGS_CSV_IMPL_FLAGS)
1220
1221	// Store the paths in the info for use by other modules and sdk snapshot generation.
1222	return HiddenAPIFlagOutput{
1223		AnnotationFlagsPath:   annotationFlagsCSV,
1224		MetadataPath:          metadataCSV,
1225		IndexPath:             indexCSV,
1226		StubFlagsPath:         stubFlagsCSV,
1227		AllFlagsPath:          allFlagsCSV,
1228		FilteredStubFlagsPath: filteredStubFlagsCSV,
1229		FilteredFlagsPath:     filteredFlagsCSV,
1230	}
1231}
1232
1233// hiddenAPIEncodeRulesForBootclasspathFragment generates rules to encode hidden API flags into the
1234// dex jars in bootDexInfoByModule.
1235func hiddenAPIEncodeRulesForBootclasspathFragment(ctx android.ModuleContext, bootDexInfoByModule bootDexInfoByModule, allFlagsCSV android.Path) bootDexJarByModule {
1236	// Encode the flags into the boot dex files.
1237	encodedBootDexJarsByModule := bootDexJarByModule{}
1238	outputDir := android.PathForModuleOut(ctx, "hiddenapi-modular/encoded").OutputPath
1239	for _, name := range android.SortedKeys(bootDexInfoByModule) {
1240		bootDexInfo := bootDexInfoByModule[name]
1241		unencodedDex := bootDexInfo.path
1242		encodedDex := hiddenAPIEncodeDex(ctx, unencodedDex, allFlagsCSV, bootDexInfo.uncompressDex, bootDexInfo.minSdkVersion, outputDir)
1243		encodedBootDexJarsByModule[name] = encodedDex
1244	}
1245	return encodedBootDexJarsByModule
1246}
1247
1248func buildRuleToGenerateRemovedDexSignatures(ctx android.ModuleContext, suffix string, removedTxtFiles android.Paths) android.OptionalPath {
1249	if len(removedTxtFiles) == 0 {
1250		return android.OptionalPath{}
1251	}
1252
1253	output := android.PathForModuleOut(ctx, "module-hiddenapi"+suffix, "removed-dex-signatures.txt")
1254
1255	rule := android.NewRuleBuilder(pctx, ctx)
1256	rule.Command().
1257		BuiltTool("metalava").
1258		Inputs(removedTxtFiles).
1259		FlagWithOutput("--dex-api ", 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