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