1// Copyright 2018 Google Inc. All rights reserved. 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 android 16 17import ( 18 "fmt" 19 "slices" 20 "strconv" 21 "strings" 22 "sync" 23 24 "github.com/google/blueprint" 25) 26 27var ( 28 // This is the sdk version when APEX was first introduced 29 SdkVersion_Android10 = uncheckedFinalApiLevel(29) 30) 31 32// ApexInfo describes the metadata about one or more apexBundles that an apex variant of a module is 33// part of. When an apex variant is created, the variant is associated with one apexBundle. But 34// when multiple apex variants are merged for deduping (see mergeApexVariations), this holds the 35// information about the apexBundles that are merged together. 36// Accessible via `ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)` 37type ApexInfo struct { 38 // Name of the apex variation that this module (i.e. the apex variant of the module) is 39 // mutated into, or "" for a platform (i.e. non-APEX) variant. 40 // 41 // Also note that a module can be included in multiple APEXes, in which case, the module is 42 // mutated into one or more variants, each of which is for an APEX. The variants then can 43 // later be deduped if they don't need to be compiled differently. This is an optimization 44 // done in mergeApexVariations. 45 ApexVariationName string 46 47 // ApiLevel that this module has to support at minimum. 48 MinSdkVersion ApiLevel 49 50 // True if this module comes from an updatable apexBundle. 51 Updatable bool 52 53 // True if this module can use private platform APIs. Only non-updatable APEX can set this 54 // to true. 55 UsePlatformApis bool 56 57 // True if this is for a prebuilt_apex. 58 // 59 // If true then this will customize the apex processing to make it suitable for handling 60 // prebuilt_apex, e.g. it will prevent ApexInfos from being merged together. 61 // 62 // Unlike the source apex module type the prebuilt_apex module type cannot share compatible variants 63 // across prebuilt_apex modules. That is because there is no way to determine whether two 64 // prebuilt_apex modules that export files for the same module are compatible. e.g. they could have 65 // been built from different source at different times or they could have been built with different 66 // build options that affect the libraries. 67 // 68 // While it may be possible to provide sufficient information to determine whether two prebuilt_apex 69 // modules were compatible it would be a lot of work and would not provide much benefit for a couple 70 // of reasons: 71 // - The number of prebuilt_apex modules that will be exporting files for the same module will be 72 // low as the prebuilt_apex only exports files for the direct dependencies that require it and 73 // very few modules are direct dependencies of multiple prebuilt_apex modules, e.g. there are a 74 // few com.android.art* apex files that contain the same contents and could export files for the 75 // same modules but only one of them needs to do so. Contrast that with source apex modules which 76 // need apex specific variants for every module that contributes code to the apex, whether direct 77 // or indirect. 78 // - The build cost of a prebuilt_apex variant is generally low as at worst it will involve some 79 // extra copying of files. Contrast that with source apex modules that has to build each variant 80 // from source. 81 ForPrebuiltApex bool 82 83 // Returns the name of the overridden apex (com.android.foo) 84 BaseApexName string 85 86 // Returns the value of `apex_available_name` 87 ApexAvailableName string 88} 89 90func (a ApexInfo) Variation() string { 91 return a.ApexVariationName 92} 93 94// Minimize is called during a transition from a module with a unique variation per apex to a module that should 95// share variations between apexes. It returns a minimized ApexInfo that removes any apex names and replaces 96// the variation name with one computed from the remaining properties. 97func (a ApexInfo) Minimize() ApexInfo { 98 info := ApexInfo{ 99 MinSdkVersion: a.MinSdkVersion, 100 UsePlatformApis: a.UsePlatformApis, 101 } 102 info.ApexVariationName = info.mergedName() 103 return info 104} 105 106type ApexAvailableInfo struct { 107 // Returns the apex names that this module is available for 108 ApexAvailableFor []string 109} 110 111var ApexInfoProvider = blueprint.NewMutatorProvider[ApexInfo]("apex_mutate") 112var ApexAvailableInfoProvider = blueprint.NewMutatorProvider[ApexAvailableInfo]("apex_mutate") 113 114func (i ApexInfo) AddJSONData(d *map[string]interface{}) { 115 (*d)["Apex"] = map[string]interface{}{ 116 "ApexVariationName": i.ApexVariationName, 117 "MinSdkVersion": i.MinSdkVersion, 118 "ForPrebuiltApex": i.ForPrebuiltApex, 119 } 120} 121 122// mergedName gives the name of the alias variation that will be used when multiple apex variations 123// of a module can be deduped into one variation. For example, if libfoo is included in both apex.a 124// and apex.b, and if the two APEXes have the same min_sdk_version (say 29), then libfoo doesn't 125// have to be built twice, but only once. In that case, the two apex variations apex.a and apex.b 126// are configured to have the same alias variation named apex29. Whether platform APIs is allowed 127// or not also matters; if two APEXes don't have the same allowance, they get different names and 128// thus wouldn't be merged. 129func (i ApexInfo) mergedName() string { 130 name := "apex" + strconv.Itoa(i.MinSdkVersion.FinalOrFutureInt()) 131 if i.UsePlatformApis { 132 name += "_p" 133 } 134 return name 135} 136 137// IsForPlatform tells whether this module is for the platform or not. If false is returned, it 138// means that this apex variant of the module is built for an APEX. 139func (i ApexInfo) IsForPlatform() bool { 140 return i.ApexVariationName == "" 141} 142 143// To satisfy the comparable interface 144func (i ApexInfo) Equal(other any) bool { 145 otherApexInfo, ok := other.(ApexInfo) 146 return ok && i.ApexVariationName == otherApexInfo.ApexVariationName && 147 i.MinSdkVersion == otherApexInfo.MinSdkVersion && 148 i.Updatable == otherApexInfo.Updatable && 149 i.UsePlatformApis == otherApexInfo.UsePlatformApis 150} 151 152// ApexBundleInfo contains information about the dependencies of an apex 153type ApexBundleInfo struct { 154} 155 156var ApexBundleInfoProvider = blueprint.NewMutatorProvider[ApexBundleInfo]("apex_mutate") 157 158// DepInSameApexChecker defines an interface that should be used to determine whether a given dependency 159// should be considered as part of the same APEX as the current module or not. 160type DepInSameApexChecker interface { 161 // OutgoingDepIsInSameApex tests if the module depended on via 'tag' is considered as part of 162 // the same APEX as this module. For example, a static lib dependency usually returns true here, while a 163 // shared lib dependency to a stub library returns false. 164 // 165 // This method must not be called directly without first ignoring dependencies whose tags 166 // implement ExcludeFromApexContentsTag. Calls from within the func passed to WalkPayloadDeps() 167 // are fine as WalkPayloadDeps() will ignore those dependencies automatically. Otherwise, use 168 // IsDepInSameApex instead. 169 OutgoingDepIsInSameApex(tag blueprint.DependencyTag) bool 170 171 // IncomingDepIsInSameApex tests if this module depended on via 'tag' is considered as part of 172 // the same APEX as the depending module module. For example, a static lib dependency usually 173 // returns true here, while a shared lib dependency to a stub library returns false. 174 // 175 // This method must not be called directly without first ignoring dependencies whose tags 176 // implement ExcludeFromApexContentsTag. Calls from within the func passed to WalkPayloadDeps() 177 // are fine as WalkPayloadDeps() will ignore those dependencies automatically. Otherwise, use 178 // IsDepInSameApex instead. 179 IncomingDepIsInSameApex(tag blueprint.DependencyTag) bool 180} 181 182// DepInSameApexInfo is a provider that wraps around a DepInSameApexChecker that can be 183// used to check if a dependency belongs to the same apex as the module when walking 184// through the dependencies of a module. 185type DepInSameApexInfo struct { 186 Checker DepInSameApexChecker 187} 188 189var DepInSameApexInfoProvider = blueprint.NewMutatorProvider[DepInSameApexInfo]("apex_unique") 190 191func IsDepInSameApex(ctx BaseModuleContext, module, dep Module) bool { 192 depTag := ctx.OtherModuleDependencyTag(dep) 193 if _, ok := depTag.(ExcludeFromApexContentsTag); ok { 194 // The tag defines a dependency that never requires the child module to be part of the same 195 // apex as the parent. 196 return false 197 } 198 199 if !EqualModules(ctx.Module(), module) { 200 if moduleInfo, ok := OtherModuleProvider(ctx, module, DepInSameApexInfoProvider); ok { 201 if !moduleInfo.Checker.OutgoingDepIsInSameApex(depTag) { 202 return false 203 } 204 } 205 } else { 206 if m, ok := ctx.Module().(ApexModule); ok && !m.GetDepInSameApexChecker().OutgoingDepIsInSameApex(depTag) { 207 return false 208 } 209 } 210 if depInfo, ok := OtherModuleProvider(ctx, dep, DepInSameApexInfoProvider); ok { 211 if !depInfo.Checker.IncomingDepIsInSameApex(depTag) { 212 return false 213 } 214 } 215 216 return true 217} 218 219// ApexModule is the interface that a module type is expected to implement if the module has to be 220// built differently depending on whether the module is destined for an APEX or not (i.e., installed 221// to one of the regular partitions). 222// 223// Native shared libraries are one such module type; when it is built for an APEX, it should depend 224// only on stable interfaces such as NDK, stable AIDL, or C APIs from other APEXes. 225// 226// A module implementing this interface will be mutated into multiple variations by apex.apexMutator 227// if it is directly or indirectly included in one or more APEXes. Specifically, if a module is 228// included in apex.foo and apex.bar then three apex variants are created: platform, apex.foo and 229// apex.bar. The platform variant is for the regular partitions (e.g., /system or /vendor, etc.) 230// while the other two are for the APEXs, respectively. The latter two variations can be merged (see 231// mergedName) when the two APEXes have the same min_sdk_version requirement. 232type ApexModule interface { 233 Module 234 235 apexModuleBase() *ApexModuleBase 236 237 // Marks that this module should be built for the specified APEX. Call this BEFORE 238 // apex.apexMutator is run. 239 BuildForApex(apex ApexInfo) 240 241 // Returns true if this module is present in any APEX either directly or indirectly. Call 242 // this after apex.apexMutator is run. 243 InAnyApex() bool 244 245 // NotInPlatform returns true if the module is not available to the platform due to 246 // apex_available being set and not containing "//apex_available:platform". 247 NotInPlatform() bool 248 249 // Tests if this module could have APEX variants. Even when a module type implements 250 // ApexModule interface, APEX variants are created only for the module instances that return 251 // true here. This is useful for not creating APEX variants for certain types of shared 252 // libraries such as NDK stubs. 253 CanHaveApexVariants() bool 254 255 // Tests if this module can be installed to APEX as a file. For example, this would return 256 // true for shared libs while return false for static libs because static libs are not 257 // installable module (but it can still be mutated for APEX) 258 IsInstallableToApex() bool 259 260 // Tests if this module is available for the specified APEX or ":platform". This is from the 261 // apex_available property of the module. 262 AvailableFor(what string) bool 263 264 // Returns the apexes that are available for this module, valid values include 265 // "//apex_available:platform", "//apex_available:anyapex" and specific apexes. 266 // There are some differences between this one and the ApexAvailable on 267 // ApexModuleBase for cc, java library and sdkLibraryXml. 268 ApexAvailableFor() []string 269 270 // AlwaysRequiresPlatformApexVariant allows the implementing module to determine whether an 271 // APEX mutator should always be created for it. 272 // 273 // Returns false by default. 274 AlwaysRequiresPlatformApexVariant() bool 275 276 // Returns true if this module is not available to platform (i.e. apex_available property 277 // doesn't have "//apex_available:platform"), or shouldn't be available to platform, which 278 // is the case when this module depends on other module that isn't available to platform. 279 NotAvailableForPlatform() bool 280 281 // Marks that this module is not available to platform. Set by the 282 // check-platform-availability mutator in the apex package. 283 SetNotAvailableForPlatform() 284 285 // Returns the min sdk version that the module supports, . 286 MinSdkVersionSupported(ctx BaseModuleContext) ApiLevel 287 288 // Returns true if this module needs a unique variation per apex, effectively disabling the 289 // deduping. This is turned on when, for example if use_apex_name_macro is set so that each 290 // apex variant should be built with different macro definitions. 291 UniqueApexVariations() bool 292 293 GetDepInSameApexChecker() DepInSameApexChecker 294} 295 296// Properties that are common to all module types implementing ApexModule interface. 297type ApexProperties struct { 298 // Availability of this module in APEXes. Only the listed APEXes can contain this module. If 299 // the module has stubs then other APEXes and the platform may access it through them 300 // (subject to visibility). 301 // 302 // "//apex_available:anyapex" is a pseudo APEX name that matches to any APEX. 303 // "//apex_available:platform" refers to non-APEX partitions like "system.img". 304 // Prefix pattern (com.foo.*) can be used to match with any APEX name with the prefix(com.foo.). 305 // Default is ["//apex_available:platform"]. 306 Apex_available []string 307 308 // See ApexModule.NotAvailableForPlatform() 309 NotAvailableForPlatform bool `blueprint:"mutated"` 310 311 // See ApexModule.UniqueApexVariants() 312 UniqueApexVariationsForDeps bool `blueprint:"mutated"` 313} 314 315// Marker interface that identifies dependencies that are excluded from APEX contents. 316// 317// At the moment the sdk.sdkRequirementsMutator relies on the fact that the existing tags which 318// implement this interface do not define dependencies onto members of an sdk_snapshot. If that 319// changes then sdk.sdkRequirementsMutator will need fixing. 320type ExcludeFromApexContentsTag interface { 321 blueprint.DependencyTag 322 323 // Method that differentiates this interface from others. 324 ExcludeFromApexContents() 325} 326 327// Interface that identifies dependencies to skip Apex dependency check 328type SkipApexAllowedDependenciesCheck interface { 329 // Returns true to skip the Apex dependency check, which limits the allowed dependency in build. 330 SkipApexAllowedDependenciesCheck() bool 331} 332 333// ApexModuleBase provides the default implementation for the ApexModule interface. APEX-aware 334// modules are expected to include this struct and call InitApexModule(). 335type ApexModuleBase struct { 336 ApexProperties ApexProperties 337 apexPropertiesLock sync.Mutex // protects ApexProperties during parallel apexDirectlyInAnyMutator 338 339 canHaveApexVariants bool 340 341 apexInfos []ApexInfo 342 apexInfosLock sync.Mutex // protects apexInfos during parallel apexInfoMutator 343} 344 345func (m *ApexModuleBase) ApexTransitionMutatorSplit(ctx BaseModuleContext) []ApexInfo { 346 return []ApexInfo{{}} 347} 348 349func (m *ApexModuleBase) ApexTransitionMutatorOutgoing(ctx OutgoingTransitionContext, info ApexInfo) ApexInfo { 350 if !ctx.Module().(ApexModule).GetDepInSameApexChecker().OutgoingDepIsInSameApex(ctx.DepTag()) { 351 return ApexInfo{} 352 } 353 return info 354} 355 356func (m *ApexModuleBase) ApexTransitionMutatorIncoming(ctx IncomingTransitionContext, info ApexInfo) ApexInfo { 357 module := ctx.Module().(ApexModule) 358 if !module.CanHaveApexVariants() { 359 return ApexInfo{} 360 } 361 362 if !ctx.Module().(ApexModule).GetDepInSameApexChecker().IncomingDepIsInSameApex(ctx.DepTag()) { 363 return ApexInfo{} 364 } 365 366 if info.ApexVariationName == "" { 367 return ApexInfo{} 368 } 369 370 if !ctx.Module().(ApexModule).UniqueApexVariations() && !m.ApexProperties.UniqueApexVariationsForDeps && !info.ForPrebuiltApex { 371 return info.Minimize() 372 } 373 return info 374} 375 376func (m *ApexModuleBase) ApexTransitionMutatorMutate(ctx BottomUpMutatorContext, info ApexInfo) { 377 SetProvider(ctx, ApexInfoProvider, info) 378 379 module := ctx.Module().(ApexModule) 380 base := module.apexModuleBase() 381 382 platformVariation := info.ApexVariationName == "" 383 if !platformVariation { 384 // Do some validity checks. 385 // TODO(jiyong): is this the right place? 386 base.checkApexAvailableProperty(ctx) 387 388 SetProvider(ctx, ApexAvailableInfoProvider, ApexAvailableInfo{ 389 ApexAvailableFor: module.ApexAvailableFor(), 390 }) 391 } 392 if platformVariation && !ctx.Host() && !module.AvailableFor(AvailableToPlatform) && module.NotAvailableForPlatform() { 393 // Do not install the module for platform, but still allow it to output 394 // uninstallable AndroidMk entries in certain cases when they have side 395 // effects. TODO(jiyong): move this routine to somewhere else 396 module.MakeUninstallable() 397 } 398} 399 400// Initializes ApexModuleBase struct. Not calling this (even when inheriting from ApexModuleBase) 401// prevents the module from being mutated for apexBundle. 402func InitApexModule(m ApexModule) { 403 base := m.apexModuleBase() 404 base.canHaveApexVariants = true 405 406 m.AddProperties(&base.ApexProperties) 407} 408 409// Implements ApexModule 410func (m *ApexModuleBase) apexModuleBase() *ApexModuleBase { 411 return m 412} 413 414var ( 415 availableToPlatformList = []string{AvailableToPlatform} 416) 417 418// Implements ApexModule 419func (m *ApexModuleBase) ApexAvailable() []string { 420 aa := m.ApexProperties.Apex_available 421 if len(aa) > 0 { 422 return aa 423 } 424 // Default is availability to platform 425 return CopyOf(availableToPlatformList) 426} 427 428func (m *ApexModuleBase) ApexAvailableFor() []string { 429 return m.ApexAvailable() 430} 431 432// Implements ApexModule 433func (m *ApexModuleBase) BuildForApex(apex ApexInfo) { 434 m.apexInfosLock.Lock() 435 defer m.apexInfosLock.Unlock() 436 if slices.ContainsFunc(m.apexInfos, func(existing ApexInfo) bool { 437 return existing.ApexVariationName == apex.ApexVariationName 438 }) { 439 return 440 } 441 m.apexInfos = append(m.apexInfos, apex) 442} 443 444// Implements ApexModule 445func (m *ApexModuleBase) InAnyApex() bool { 446 for _, apex_name := range m.ApexProperties.Apex_available { 447 if apex_name != AvailableToPlatform { 448 return true 449 } 450 } 451 return false 452} 453 454// Implements ApexModule 455func (m *ApexModuleBase) NotInPlatform() bool { 456 return !m.AvailableFor(AvailableToPlatform) 457} 458 459// Implements ApexModule 460func (m *ApexModuleBase) CanHaveApexVariants() bool { 461 return m.canHaveApexVariants 462} 463 464// Implements ApexModule 465func (m *ApexModuleBase) IsInstallableToApex() bool { 466 // If needed, this will bel overridden by concrete types inheriting 467 // ApexModuleBase 468 return false 469} 470 471// Implements ApexModule 472func (m *ApexModuleBase) UniqueApexVariations() bool { 473 // If needed, this will bel overridden by concrete types inheriting 474 // ApexModuleBase 475 return false 476} 477 478// Implements ApexModule 479func (m *ApexModuleBase) GetDepInSameApexChecker() DepInSameApexChecker { 480 return BaseDepInSameApexChecker{} 481} 482 483type BaseDepInSameApexChecker struct{} 484 485func (m BaseDepInSameApexChecker) OutgoingDepIsInSameApex(tag blueprint.DependencyTag) bool { 486 return true 487} 488 489func (m BaseDepInSameApexChecker) IncomingDepIsInSameApex(tag blueprint.DependencyTag) bool { 490 return true 491} 492 493const ( 494 AvailableToPlatform = "//apex_available:platform" 495 AvailableToAnyApex = "//apex_available:anyapex" 496) 497 498// CheckAvailableForApex provides the default algorithm for checking the apex availability. When the 499// availability is empty, it defaults to ["//apex_available:platform"] which means "available to the 500// platform but not available to any APEX". When the list is not empty, `what` is matched against 501// the list. If there is any matching element in the list, thus function returns true. The special 502// availability "//apex_available:anyapex" matches with anything except for 503// "//apex_available:platform". 504func CheckAvailableForApex(what string, apex_available []string) bool { 505 if len(apex_available) == 0 { 506 return what == AvailableToPlatform 507 } 508 509 // TODO b/248601389 510 if what == "com.google.mainline.primary.libs" || what == "com.google.mainline.go.primary.libs" { 511 return true 512 } 513 514 for _, apex_name := range apex_available { 515 // exact match. 516 if apex_name == what { 517 return true 518 } 519 // //apex_available:anyapex matches with any apex name, but not //apex_available:platform 520 if apex_name == AvailableToAnyApex && what != AvailableToPlatform { 521 return true 522 } 523 // prefix match. 524 if strings.HasSuffix(apex_name, ".*") && strings.HasPrefix(what, strings.TrimSuffix(apex_name, "*")) { 525 return true 526 } 527 // TODO b/383863941: Remove once legacy name is no longer used 528 if (apex_name == "com.android.btservices" && what == "com.android.bt") || (apex_name == "com.android.bt" && what == "com.android.btservices") { 529 return true 530 } 531 } 532 return false 533} 534 535// Implements ApexModule 536func (m *ApexModuleBase) AvailableFor(what string) bool { 537 return CheckAvailableForApex(what, m.ApexAvailableFor()) 538} 539 540// Implements ApexModule 541func (m *ApexModuleBase) AlwaysRequiresPlatformApexVariant() bool { 542 return false 543} 544 545// Implements ApexModule 546func (m *ApexModuleBase) NotAvailableForPlatform() bool { 547 return m.ApexProperties.NotAvailableForPlatform 548} 549 550// Implements ApexModule 551func (m *ApexModuleBase) SetNotAvailableForPlatform() { 552 m.ApexProperties.NotAvailableForPlatform = true 553} 554 555// This function makes sure that the apex_available property is valid 556func (m *ApexModuleBase) checkApexAvailableProperty(mctx BaseModuleContext) { 557 for _, n := range m.ApexProperties.Apex_available { 558 if n == AvailableToPlatform || n == AvailableToAnyApex { 559 continue 560 } 561 // Prefix pattern should end with .* and has at least two components. 562 if strings.Contains(n, "*") { 563 if !strings.HasSuffix(n, ".*") { 564 mctx.PropertyErrorf("apex_available", "Wildcard should end with .* like com.foo.*") 565 } 566 if strings.Count(n, ".") < 2 { 567 mctx.PropertyErrorf("apex_available", "Wildcard requires two or more components like com.foo.*") 568 } 569 if strings.Count(n, "*") != 1 { 570 mctx.PropertyErrorf("apex_available", "Wildcard is not allowed in the middle.") 571 } 572 continue 573 } 574 if !mctx.OtherModuleExists(n) && !mctx.Config().AllowMissingDependencies() { 575 mctx.PropertyErrorf("apex_available", "%q is not a valid module name", n) 576 } 577 } 578} 579 580// AvailableToSameApexes returns true if the two modules are apex_available to 581// exactly the same set of APEXes (and platform), i.e. if their apex_available 582// properties have the same elements. 583func AvailableToSameApexes(mod1, mod2 ApexModule) bool { 584 mod1ApexAvail := SortedUniqueStrings(mod1.apexModuleBase().ApexProperties.Apex_available) 585 mod2ApexAvail := SortedUniqueStrings(mod2.apexModuleBase().ApexProperties.Apex_available) 586 if len(mod1ApexAvail) != len(mod2ApexAvail) { 587 return false 588 } 589 for i, v := range mod1ApexAvail { 590 if v != mod2ApexAvail[i] { 591 return false 592 } 593 } 594 return true 595} 596 597// UpdateUniqueApexVariationsForDeps sets UniqueApexVariationsForDeps if any dependencies that are 598// in the same APEX have unique APEX variations so that the module can link against the right 599// variant. 600func UpdateUniqueApexVariationsForDeps(mctx BottomUpMutatorContext, am ApexModule) { 601 // If any of the dependencies requires unique apex variations, so does this module. 602 mctx.VisitDirectDeps(func(dep Module) { 603 if depApexModule, ok := dep.(ApexModule); ok { 604 if IsDepInSameApex(mctx, am, depApexModule) && 605 (depApexModule.UniqueApexVariations() || 606 depApexModule.apexModuleBase().ApexProperties.UniqueApexVariationsForDeps) { 607 am.apexModuleBase().ApexProperties.UniqueApexVariationsForDeps = true 608 } 609 } 610 }) 611} 612 613//////////////////////////////////////////////////////////////////////////////////////////////////// 614//Below are routines for extra safety checks. 615// 616// BuildDepsInfoLists is to flatten the dependency graph for an apexBundle into a text file 617// (actually two in slightly different formats). The files are mostly for debugging, for example to 618// see why a certain module is included in an APEX via which dependency path. 619// 620// CheckMinSdkVersion is to make sure that all modules in an apexBundle satisfy the min_sdk_version 621// requirement of the apexBundle. 622 623// A dependency info for a single ApexModule, either direct or transitive. 624type ApexModuleDepInfo struct { 625 // Name of the dependency 626 To string 627 // List of dependencies To belongs to. Includes APEX itself, if a direct dependency. 628 From []string 629 // Whether the dependency belongs to the final compiled APEX. 630 IsExternal bool 631 // min_sdk_version of the ApexModule 632 MinSdkVersion string 633} 634 635// A map of a dependency name to its ApexModuleDepInfo 636type DepNameToDepInfoMap map[string]ApexModuleDepInfo 637 638type ApexBundleDepsInfo struct { 639 flatListPath Path 640 fullListPath Path 641} 642 643type ApexBundleDepsInfoIntf interface { 644 Updatable() bool 645 FlatListPath() Path 646 FullListPath() Path 647} 648 649type ApexBundleDepsData struct { 650 Updatable bool 651 FlatListPath Path 652} 653 654var ApexBundleDepsDataProvider = blueprint.NewProvider[ApexBundleDepsData]() 655 656func (d *ApexBundleDepsInfo) FlatListPath() Path { 657 return d.flatListPath 658} 659 660func (d *ApexBundleDepsInfo) FullListPath() Path { 661 return d.fullListPath 662} 663 664// Generate two module out files: 665// 1. FullList with transitive deps and their parents in the dep graph 666// 2. FlatList with a flat list of transitive deps 667// In both cases transitive deps of external deps are not included. Neither are deps that are only 668// available to APEXes; they are developed with updatability in mind and don't need manual approval. 669func (d *ApexBundleDepsInfo) BuildDepsInfoLists(ctx ModuleContext, minSdkVersion string, depInfos DepNameToDepInfoMap) { 670 var fullContent strings.Builder 671 var flatContent strings.Builder 672 673 fmt.Fprintf(&fullContent, "%s(minSdkVersion:%s):\n", ctx.ModuleName(), minSdkVersion) 674 for _, key := range FirstUniqueStrings(SortedKeys(depInfos)) { 675 info := depInfos[key] 676 toName := fmt.Sprintf("%s(minSdkVersion:%s)", info.To, info.MinSdkVersion) 677 if info.IsExternal { 678 toName = toName + " (external)" 679 } 680 fmt.Fprintf(&fullContent, " %s <- %s\n", toName, strings.Join(SortedUniqueStrings(info.From), ", ")) 681 fmt.Fprintf(&flatContent, "%s\n", toName) 682 } 683 684 fullListPath := PathForModuleOut(ctx, "depsinfo", "fulllist.txt") 685 WriteFileRule(ctx, fullListPath, fullContent.String()) 686 d.fullListPath = fullListPath 687 688 flatListPath := PathForModuleOut(ctx, "depsinfo", "flatlist.txt") 689 WriteFileRule(ctx, flatListPath, flatContent.String()) 690 d.flatListPath = flatListPath 691 692 ctx.Phony(fmt.Sprintf("%s-depsinfo", ctx.ModuleName()), fullListPath, flatListPath) 693} 694 695// Function called while walking an APEX's payload dependencies. 696// 697// Return true if the `to` module should be visited, false otherwise. 698type PayloadDepsCallback func(ctx BaseModuleContext, from, to ModuleProxy, externalDep bool) bool 699type WalkPayloadDepsFunc func(ctx BaseModuleContext, do PayloadDepsCallback) 700 701// ModuleWithMinSdkVersionCheck represents a module that implements min_sdk_version checks 702type ModuleWithMinSdkVersionCheck interface { 703 Module 704 MinSdkVersion(ctx EarlyModuleContext) ApiLevel 705 CheckMinSdkVersion(ctx ModuleContext) 706} 707 708// CheckMinSdkVersion checks if every dependency of an updatable module sets min_sdk_version 709// accordingly 710func CheckMinSdkVersion(ctx ModuleContext, minSdkVersion ApiLevel, walk WalkPayloadDepsFunc) { 711 // do not enforce min_sdk_version for host 712 if ctx.Host() { 713 return 714 } 715 716 // do not enforce for coverage build 717 if ctx.Config().IsEnvTrue("EMMA_INSTRUMENT") || ctx.DeviceConfig().NativeCoverageEnabled() || ctx.DeviceConfig().ClangCoverageEnabled() { 718 return 719 } 720 721 // do not enforce deps.min_sdk_version if APEX/APK doesn't set min_sdk_version 722 if minSdkVersion.IsNone() { 723 return 724 } 725 726 walk(ctx, func(ctx BaseModuleContext, from, to ModuleProxy, externalDep bool) bool { 727 if externalDep { 728 // external deps are outside the payload boundary, which is "stable" 729 // interface. We don't have to check min_sdk_version for external 730 // dependencies. 731 return false 732 } 733 if !IsDepInSameApex(ctx, from, to) { 734 return false 735 } 736 if info, ok := OtherModuleProvider(ctx, to, CommonModuleInfoProvider); ok && info.ModuleWithMinSdkVersionCheck { 737 if info.MinSdkVersion.ApiLevel == nil || !info.MinSdkVersion.ApiLevel.Specified() { 738 // This dependency performs its own min_sdk_version check, just make sure it sets min_sdk_version 739 // to trigger the check. 740 ctx.OtherModuleErrorf(to, "must set min_sdk_version") 741 } 742 return false 743 } 744 if err := ShouldSupportSdkVersion(ctx, to, minSdkVersion); err != nil { 745 ctx.OtherModuleErrorf(to, "should support min_sdk_version(%v) for %q: %v."+ 746 "\n\nDependency path: %s\n\n"+ 747 "Consider adding 'min_sdk_version: %q' to %q", 748 minSdkVersion, ctx.ModuleName(), err.Error(), 749 ctx.GetPathString(false), 750 minSdkVersion, ctx.OtherModuleName(to)) 751 return false 752 } 753 return true 754 }) 755} 756 757type MinSdkVersionFromValueContext interface { 758 Config() Config 759 DeviceConfig() DeviceConfig 760 ModuleErrorContext 761} 762 763// Returns nil (success) if this module should support the given sdk version. Returns an 764// error if not. No default implementation is provided for this method. A module type 765// implementing this interface should provide an implementation. A module supports an sdk 766// version when the module's min_sdk_version is equal to or less than the given sdk version. 767func ShouldSupportSdkVersion(ctx BaseModuleContext, module Module, sdkVersion ApiLevel) error { 768 info, ok := OtherModuleProvider(ctx, module, CommonModuleInfoProvider) 769 if !ok || info.MinSdkVersionSupported.IsNone() { 770 return fmt.Errorf("min_sdk_version is not specified") 771 } 772 minVer := info.MinSdkVersionSupported 773 774 if minVer.GreaterThan(sdkVersion) { 775 return fmt.Errorf("newer SDK(%v)", minVer) 776 } 777 778 return nil 779} 780 781// Construct ApiLevel object from min_sdk_version string value 782func MinSdkVersionFromValue(ctx MinSdkVersionFromValueContext, value string) ApiLevel { 783 if value == "" { 784 return NoneApiLevel 785 } 786 apiLevel, err := ApiLevelFromUser(ctx, value) 787 if err != nil { 788 ctx.PropertyErrorf("min_sdk_version", "%s", err.Error()) 789 return NoneApiLevel 790 } 791 return apiLevel 792} 793 794var ApexExportsInfoProvider = blueprint.NewProvider[ApexExportsInfo]() 795 796// ApexExportsInfo contains information about the artifacts provided by apexes to dexpreopt and hiddenapi 797type ApexExportsInfo struct { 798 // Canonical name of this APEX. Used to determine the path to the activated APEX on 799 // device (/apex/<apex_name>) 800 ApexName string 801 802 // Path to the image profile file on host (or empty, if profile is not generated). 803 ProfilePathOnHost Path 804 805 // Map from the apex library name (without prebuilt_ prefix) to the dex file path on host 806 LibraryNameToDexJarPathOnHost map[string]Path 807} 808 809var PrebuiltInfoProvider = blueprint.NewProvider[PrebuiltInfo]() 810 811// contents of prebuilt_info.json 812type PrebuiltInfo struct { 813 // Name of the apex, without the prebuilt_ prefix 814 Name string 815 816 Is_prebuilt bool 817 818 // This is relative to root of the workspace. 819 // In case of mainline modules, this file contains the build_id that was used 820 // to generate the mainline module prebuilt. 821 Prebuilt_info_file_path string `json:",omitempty"` 822} 823 824// FragmentInApexTag is embedded into a dependency tag to allow apex modules to annotate 825// their fragments in a way that allows the java bootclasspath modules to traverse from 826// the apex to the fragment. 827type FragmentInApexTag struct{} 828 829func (FragmentInApexTag) isFragmentInApexTag() {} 830 831type isFragmentInApexTagIntf interface { 832 isFragmentInApexTag() 833} 834 835// IsFragmentInApexTag returns true if the dependency tag embeds FragmentInApexTag, 836// signifying that it is a dependency from an apex module to its fragment. 837func IsFragmentInApexTag(tag blueprint.DependencyTag) bool { 838 _, ok := tag.(isFragmentInApexTagIntf) 839 return ok 840} 841