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