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