1// Copyright 2020 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 java 16 17// This file contains the module implementations for android_app_import and android_test_import. 18 19import ( 20 "reflect" 21 "strings" 22 23 "github.com/google/blueprint" 24 25 "github.com/google/blueprint/proptools" 26 27 "android/soong/android" 28 "android/soong/provenance" 29) 30 31func init() { 32 RegisterAppImportBuildComponents(android.InitRegistrationContext) 33 34 initAndroidAppImportVariantGroupTypes() 35} 36 37var ( 38 uncompressEmbeddedJniLibsRule = pctx.AndroidStaticRule("uncompress-embedded-jni-libs", blueprint.RuleParams{ 39 Command: `if (zipinfo $in 'lib/*.so' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then ` + 40 `${config.Zip2ZipCmd} -i $in -o $out -0 'lib/**/*.so'` + 41 `; else cp -f $in $out; fi`, 42 CommandDeps: []string{"${config.Zip2ZipCmd}"}, 43 Description: "Uncompress embedded JNI libs", 44 }) 45 46 stripEmbeddedJniLibsUnusedArchRule = pctx.AndroidStaticRule("strip-embedded-jni-libs-from-unused-arch", blueprint.RuleParams{ 47 Command: `${config.Zip2ZipCmd} -i $in -o $out -x 'lib/**/*.so' $extraArgs`, 48 CommandDeps: []string{"${config.Zip2ZipCmd}"}, 49 Description: "Remove all JNI libs from unused architectures", 50 }, "extraArgs") 51 52 uncompressDexRule = pctx.AndroidStaticRule("uncompress-dex", blueprint.RuleParams{ 53 Command: `if (zipinfo $in '*.dex' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then ` + 54 `${config.Zip2ZipCmd} -i $in -o $out -0 'classes*.dex'` + 55 `; else cp -f $in $out; fi`, 56 CommandDeps: []string{"${config.Zip2ZipCmd}"}, 57 Description: "Uncompress dex files", 58 }) 59 60 checkPresignedApkRule = pctx.AndroidStaticRule("check-presigned-apk", blueprint.RuleParams{ 61 Command: "build/soong/scripts/check_prebuilt_presigned_apk.py --aapt2 ${config.Aapt2Cmd} --zipalign ${config.ZipAlign} $extraArgs $in $out", 62 CommandDeps: []string{"build/soong/scripts/check_prebuilt_presigned_apk.py", "${config.Aapt2Cmd}", "${config.ZipAlign}"}, 63 Description: "Check presigned apk", 64 }, "extraArgs") 65 66 extractApkRule = pctx.AndroidStaticRule("extract-apk", blueprint.RuleParams{ 67 Command: "unzip -p $in $extract_apk > $out", 68 Description: "Extract specific sub apk", 69 }, "extract_apk") 70 71 gzipRule = pctx.AndroidStaticRule("gzip", 72 blueprint.RuleParams{ 73 Command: "prebuilts/build-tools/path/linux-x86/gzip -9 -c $in > $out", 74 CommandDeps: []string{"prebuilts/build-tools/path/linux-x86/gzip"}, 75 Description: "gzip $out", 76 }) 77) 78 79func RegisterAppImportBuildComponents(ctx android.RegistrationContext) { 80 ctx.RegisterModuleType("android_app_import", AndroidAppImportFactory) 81 ctx.RegisterModuleType("android_test_import", AndroidTestImportFactory) 82 ctx.PreArchMutators(func(ctx android.RegisterMutatorsContext) { 83 ctx.BottomUp("disable_prebuilts_without_apk", disablePrebuiltsWithoutApkMutator) 84 }) 85} 86 87type AndroidAppImport struct { 88 android.ModuleBase 89 android.DefaultableModuleBase 90 android.ApexModuleBase 91 prebuilt android.Prebuilt 92 93 properties AndroidAppImportProperties 94 dpiVariants interface{} 95 archVariants interface{} 96 arch_dpiVariants interface{} 97 98 outputFile android.Path 99 certificate Certificate 100 101 dexpreopter 102 103 usesLibrary usesLibrary 104 105 installPath android.InstallPath 106 107 hideApexVariantFromMake bool 108 109 provenanceMetaDataFile android.Path 110} 111 112type AndroidAppImportProperties struct { 113 // A prebuilt apk to import 114 Apk proptools.Configurable[string] `android:"path,replace_instead_of_append"` 115 116 // The name of a certificate in the default certificate directory or an android_app_certificate 117 // module name in the form ":module". Should be empty if presigned or default_dev_cert is set. 118 Certificate proptools.Configurable[string] `android:"replace_instead_of_append"` 119 120 // Names of extra android_app_certificate modules to sign the apk with in the form ":module". 121 Additional_certificates []string 122 123 // Set this flag to true if the prebuilt apk is already signed. The certificate property must not 124 // be set for presigned modules. 125 Presigned *bool 126 127 // Name of the signing certificate lineage file or filegroup module. 128 Lineage *string `android:"path"` 129 130 // For overriding the --rotation-min-sdk-version property of apksig 131 RotationMinSdkVersion *string 132 133 // Sign with the default system dev certificate. Must be used judiciously. Most imported apps 134 // need to either specify a specific certificate or be presigned. 135 Default_dev_cert *bool 136 137 // Specifies that this app should be installed to the priv-app directory, 138 // where the system will grant it additional privileges not available to 139 // normal apps. 140 Privileged *bool 141 142 // Names of modules to be overridden. Listed modules can only be other binaries 143 // (in Make or Soong). 144 // This does not completely prevent installation of the overridden binaries, but if both 145 // binaries would be installed by default (in PRODUCT_PACKAGES) the other binary will be removed 146 // from PRODUCT_PACKAGES. 147 Overrides []string 148 149 // Optional name for the installed app. If unspecified, it is derived from the module name. 150 Filename *string 151 152 // If set, create package-export.apk, which other packages can 153 // use to get PRODUCT-agnostic resource data like IDs and type definitions. 154 Export_package_resources *bool 155 156 // Optional. Install to a subdirectory of the default install path for the module 157 Relative_install_path *string 158 159 // Whether the prebuilt apk can be installed without additional processing. Default is false. 160 Preprocessed *bool 161 162 // Whether or not to skip checking the preprocessed apk for proper alignment and uncompressed 163 // JNI libs and dex files. Default is false 164 Skip_preprocessed_apk_checks *bool 165 166 // Name of the source soong module that gets shadowed by this prebuilt 167 // If unspecified, follows the naming convention that the source module of 168 // the prebuilt is Name() without "prebuilt_" prefix 169 Source_module_name *string 170 171 // Whether stripping all libraries from unused architectures. 172 Strip_unused_jni_arch *bool 173 174 // Path to the .prebuilt_info file of the prebuilt app. 175 // In case of mainline modules, the .prebuilt_info file contains the build_id that was used 176 // to generate the prebuilt. 177 Prebuilt_info *string `android:"path"` 178 179 // Path of extracted apk which is extracted from prebuilt apk. Use this extracted to import. 180 Extract_apk proptools.Configurable[string] 181 182 // Compress the output APK using gzip. Defaults to false. 183 Compress_apk proptools.Configurable[bool] `android:"arch_variant,replace_instead_of_append"` 184} 185 186func (a *AndroidAppImport) IsInstallable() bool { 187 return true 188} 189 190// Updates properties with variant-specific values. 191// This happens as a DefaultableHook instead of a LoadHook because we want to run it after 192// soong config variables are applied. 193func (a *AndroidAppImport) processVariants(ctx android.DefaultableHookContext) { 194 config := ctx.Config() 195 dpiProps := reflect.ValueOf(a.dpiVariants).Elem().FieldByName(DpiGroupName) 196 197 // Try DPI variant matches in the reverse-priority order so that the highest priority match 198 // overwrites everything else. 199 // TODO(jungjw): Can we optimize this by making it priority order? 200 for i := len(config.ProductAAPTPrebuiltDPI()) - 1; i >= 0; i-- { 201 MergePropertiesFromVariant(ctx, &a.properties, dpiProps, config.ProductAAPTPrebuiltDPI()[i]) 202 } 203 if config.ProductAAPTPreferredConfig() != "" { 204 MergePropertiesFromVariant(ctx, &a.properties, dpiProps, config.ProductAAPTPreferredConfig()) 205 } 206 archProps := reflect.ValueOf(a.archVariants).Elem().FieldByName(ArchGroupName) 207 archType := ctx.Config().AndroidFirstDeviceTarget.Arch.ArchType 208 MergePropertiesFromVariant(ctx, &a.properties, archProps, archType.Name) 209 210 // Process "arch" includes "dpi_variants" 211 archStructPtr := reflect.ValueOf(a.arch_dpiVariants).Elem().FieldByName(ArchGroupName) 212 if archStruct := archStructPtr.Elem(); archStruct.IsValid() { 213 archPartPropsPtr := archStruct.FieldByName(proptools.FieldNameForProperty(archType.Name)) 214 if archPartProps := archPartPropsPtr.Elem(); archPartProps.IsValid() { 215 archDpiPropsPtr := archPartProps.FieldByName(DpiGroupName) 216 if archDpiProps := archDpiPropsPtr.Elem(); archDpiProps.IsValid() { 217 for i := len(config.ProductAAPTPrebuiltDPI()) - 1; i >= 0; i-- { 218 MergePropertiesFromVariant(ctx, &a.properties, archDpiProps, config.ProductAAPTPrebuiltDPI()[i]) 219 } 220 if config.ProductAAPTPreferredConfig() != "" { 221 MergePropertiesFromVariant(ctx, &a.properties, archDpiProps, config.ProductAAPTPreferredConfig()) 222 } 223 } 224 } 225 } 226} 227 228func MergePropertiesFromVariant(ctx android.EarlyModuleContext, 229 dst interface{}, variantGroup reflect.Value, variant string) { 230 src := variantGroup.FieldByName(proptools.FieldNameForProperty(variant)) 231 if !src.IsValid() { 232 return 233 } 234 235 err := proptools.ExtendMatchingProperties([]interface{}{dst}, src.Interface(), nil, proptools.OrderAppend) 236 if err != nil { 237 if propertyErr, ok := err.(*proptools.ExtendPropertyError); ok { 238 ctx.PropertyErrorf(propertyErr.Property, "%s", propertyErr.Err.Error()) 239 } else { 240 panic(err) 241 } 242 } 243} 244 245// disablePrebuiltsWithoutApkMutator is a pre-arch mutator that disables AndroidAppImport or 246// AndroidTestImport modules that don't have an apk set. We need this separate mutator instead 247// of doing it in processVariants because processVariants is a defaultable hook, and configurable 248// properties can only be evaluated after the defaults (and eventually, base configurabtion) 249// mutators. 250func disablePrebuiltsWithoutApkMutator(ctx android.BottomUpMutatorContext) { 251 switch a := ctx.Module().(type) { 252 case *AndroidAppImport: 253 if a.properties.Apk.GetOrDefault(ctx, "") == "" { 254 // Disable this module since the apk property is still empty after processing all 255 // matching variants. This likely means there is no matching variant, and the default 256 // variant doesn't have an apk property value either. 257 a.Disable() 258 } 259 case *AndroidTestImport: 260 if a.properties.Apk.GetOrDefault(ctx, "") == "" { 261 // Disable this module since the apk property is still empty after processing all 262 // matching variants. This likely means there is no matching variant, and the default 263 // variant doesn't have an apk property value either. 264 a.Disable() 265 } 266 } 267} 268 269func (a *AndroidAppImport) DepsMutator(ctx android.BottomUpMutatorContext) { 270 cert := android.SrcIsModule(a.properties.Certificate.GetOrDefault(ctx, "")) 271 if cert != "" { 272 ctx.AddDependency(ctx.Module(), certificateTag, cert) 273 } 274 275 for _, cert := range a.properties.Additional_certificates { 276 cert = android.SrcIsModule(cert) 277 if cert != "" { 278 ctx.AddDependency(ctx.Module(), certificateTag, cert) 279 } else { 280 ctx.PropertyErrorf("additional_certificates", 281 `must be names of android_app_certificate modules in the form ":module"`) 282 } 283 } 284 285 a.usesLibrary.deps(ctx, true) 286} 287 288func (a *AndroidAppImport) uncompressEmbeddedJniLibs( 289 ctx android.ModuleContext, inputPath android.Path, outputPath android.WritablePath) { 290 // Test apps don't need their JNI libraries stored uncompressed. As a matter of fact, messing 291 // with them may invalidate pre-existing signature data. 292 if ctx.InstallInTestcases() && (Bool(a.properties.Presigned) || Bool(a.properties.Preprocessed)) { 293 ctx.Build(pctx, android.BuildParams{ 294 Rule: android.Cp, 295 Output: outputPath, 296 Input: inputPath, 297 }) 298 return 299 } 300 301 ctx.Build(pctx, android.BuildParams{ 302 Rule: uncompressEmbeddedJniLibsRule, 303 Input: inputPath, 304 Output: outputPath, 305 }) 306} 307 308func (a *AndroidAppImport) extractSubApk( 309 ctx android.ModuleContext, inputPath android.Path, outputPath android.WritablePath) { 310 extractApkPath := a.properties.Extract_apk.GetOrDefault(ctx, "") 311 ctx.Build(pctx, android.BuildParams{ 312 Rule: extractApkRule, 313 Input: inputPath, 314 Output: outputPath, 315 Args: map[string]string{ 316 "extract_apk": extractApkPath, 317 }, 318 }) 319} 320 321// Returns whether this module should have the dex file stored uncompressed in the APK. 322func (a *AndroidAppImport) shouldUncompressDex(ctx android.ModuleContext) bool { 323 if ctx.Config().UnbundledBuild() || proptools.Bool(a.properties.Preprocessed) { 324 return false 325 } 326 327 // Uncompress dex in APKs of priv-apps if and only if DONT_UNCOMPRESS_PRIV_APPS_DEXS is false. 328 if a.Privileged() { 329 return ctx.Config().UncompressPrivAppDex() 330 } 331 332 return shouldUncompressDex(ctx, android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName()), &a.dexpreopter) 333} 334 335func (a *AndroidAppImport) stripEmbeddedJniLibsUnusedArch( 336 ctx android.ModuleContext, inputPath android.Path, outputPath android.WritablePath) { 337 var wantedJniLibSlice []string 338 for _, target := range ctx.MultiTargets() { 339 supported_abis := target.Arch.Abi 340 for _, arch := range supported_abis { 341 wantedJniLibSlice = append(wantedJniLibSlice, " -X 'lib/"+arch+"/*.so'") 342 } 343 } 344 wantedJniLibString := strings.Join(wantedJniLibSlice, " ") 345 ctx.Build(pctx, android.BuildParams{ 346 Rule: stripEmbeddedJniLibsUnusedArchRule, 347 Input: inputPath, 348 Output: outputPath, 349 Args: map[string]string{ 350 "extraArgs": wantedJniLibString, 351 }, 352 }) 353} 354 355func (a *AndroidAppImport) GenerateAndroidBuildActions(ctx android.ModuleContext) { 356 a.generateAndroidBuildActions(ctx) 357 358 appInfo := &AppInfo{ 359 Prebuilt: true, 360 } 361 setCommonAppInfo(appInfo, a) 362 android.SetProvider(ctx, AppInfoProvider, appInfo) 363} 364 365func (a *AndroidAppImport) InstallApkName() string { 366 return a.BaseModuleName() 367} 368 369func (a *AndroidAppImport) BaseModuleName() string { 370 return proptools.StringDefault(a.properties.Source_module_name, a.ModuleBase.Name()) 371} 372 373func (a *AndroidAppImport) generateAndroidBuildActions(ctx android.ModuleContext) { 374 if a.Name() == "prebuilt_framework-res" { 375 ctx.ModuleErrorf("prebuilt_framework-res found. This used to have special handling in soong, but was removed due to prebuilt_framework-res no longer existing. This check is to ensure it doesn't come back without readding the special handling.") 376 } 377 378 apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider) 379 if !apexInfo.IsForPlatform() { 380 a.hideApexVariantFromMake = true 381 } 382 383 if Bool(a.properties.Preprocessed) { 384 if a.properties.Presigned != nil && !*a.properties.Presigned { 385 ctx.ModuleErrorf("Setting preprocessed: true implies presigned: true, so you cannot set presigned to false") 386 } 387 t := true 388 a.properties.Presigned = &t 389 } 390 391 numCertPropsSet := 0 392 if a.properties.Certificate.GetOrDefault(ctx, "") != "" { 393 numCertPropsSet++ 394 } 395 if Bool(a.properties.Presigned) { 396 numCertPropsSet++ 397 } 398 if Bool(a.properties.Default_dev_cert) { 399 numCertPropsSet++ 400 } 401 if numCertPropsSet != 1 { 402 ctx.ModuleErrorf("One and only one of certficate, presigned (implied by preprocessed), and default_dev_cert properties must be set") 403 } 404 405 // TODO: LOCAL_PACKAGE_SPLITS 406 407 srcApk := a.prebuilt.SingleSourcePath(ctx) 408 if a.properties.Extract_apk.GetOrDefault(ctx, "") != "" { 409 extract_apk := android.PathForModuleOut(ctx, "extract-apk", ctx.ModuleName()+".apk") 410 a.extractSubApk(ctx, srcApk, extract_apk) 411 srcApk = extract_apk 412 } 413 414 // TODO: Install or embed JNI libraries 415 416 // Uncompress JNI libraries in the apk 417 jnisUncompressed := android.PathForModuleOut(ctx, "jnis-uncompressed", ctx.ModuleName()+".apk") 418 a.uncompressEmbeddedJniLibs(ctx, srcApk, jnisUncompressed) 419 420 // Strip all embedded JNI libs and include only required ones accordingly to the module's compile_multilib 421 if Bool(a.properties.Strip_unused_jni_arch) { 422 jnisStripped := android.PathForModuleOut(ctx, "jnis-stripped", ctx.ModuleName()+".apk") 423 a.stripEmbeddedJniLibsUnusedArch(ctx, jnisUncompressed, jnisStripped) 424 jnisUncompressed = jnisStripped 425 } 426 427 var pathFragments []string 428 relInstallPath := String(a.properties.Relative_install_path) 429 430 if Bool(a.properties.Privileged) { 431 pathFragments = []string{"priv-app", relInstallPath, a.BaseModuleName()} 432 } else if ctx.InstallInTestcases() { 433 pathFragments = []string{relInstallPath, a.BaseModuleName(), ctx.DeviceConfig().DeviceArch()} 434 } else { 435 pathFragments = []string{"app", relInstallPath, a.BaseModuleName()} 436 } 437 438 installDir := android.PathForModuleInstall(ctx, pathFragments...) 439 a.dexpreopter.isApp = true 440 a.dexpreopter.installPath = installDir.Join(ctx, a.BaseModuleName()+".apk") 441 a.dexpreopter.isPresignedPrebuilt = Bool(a.properties.Presigned) 442 a.dexpreopter.uncompressedDex = a.shouldUncompressDex(ctx) 443 444 a.dexpreopter.enforceUsesLibs = a.usesLibrary.enforceUsesLibraries(ctx) 445 a.dexpreopter.classLoaderContexts = a.usesLibrary.classLoaderContextForUsesLibDeps(ctx) 446 447 // Disable Dexpreopt if Compress_apk is true. It follows the build/make/core/app_prebuilt_internal.mk 448 if a.usesLibrary.shouldDisableDexpreopt || a.properties.Compress_apk.GetOrDefault(ctx, false) { 449 a.dexpreopter.disableDexpreopt() 450 } 451 452 if a.usesLibrary.enforceUsesLibraries(ctx) { 453 a.usesLibrary.verifyUsesLibrariesAPK(ctx, srcApk, &a.dexpreopter.classLoaderContexts) 454 } 455 456 a.dexpreopter.dexpreopt(ctx, android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName()), jnisUncompressed) 457 if a.dexpreopter.uncompressedDex { 458 dexUncompressed := android.PathForModuleOut(ctx, "dex-uncompressed", ctx.ModuleName()+".apk") 459 ctx.Build(pctx, android.BuildParams{ 460 Rule: uncompressDexRule, 461 Input: jnisUncompressed, 462 Output: dexUncompressed, 463 }) 464 jnisUncompressed = dexUncompressed 465 } 466 467 defaultApkFilename := a.BaseModuleName() 468 if a.properties.Compress_apk.GetOrDefault(ctx, false) { 469 defaultApkFilename += ".apk.gz" 470 } else { 471 defaultApkFilename += ".apk" 472 } 473 apkFilename := proptools.StringDefault(a.properties.Filename, defaultApkFilename) 474 475 // TODO: Handle EXTERNAL 476 477 // Sign or align the package if package has not been preprocessed 478 479 if proptools.Bool(a.properties.Preprocessed) { 480 validationStamp := a.validatePresignedApk(ctx, srcApk) 481 output := android.PathForModuleOut(ctx, apkFilename) 482 ctx.Build(pctx, android.BuildParams{ 483 Rule: android.Cp, 484 Input: srcApk, 485 Output: output, 486 Validation: validationStamp, 487 }) 488 a.outputFile = output 489 a.certificate = PresignedCertificate 490 } else if !Bool(a.properties.Presigned) { 491 // If the certificate property is empty at this point, default_dev_cert must be set to true. 492 // Which makes processMainCert's behavior for the empty cert string WAI. 493 _, _, certificates := collectAppDeps(ctx, a, false, false) 494 a.certificate, certificates = processMainCert(a.ModuleBase, a.properties.Certificate.GetOrDefault(ctx, ""), certificates, ctx) 495 signed := android.PathForModuleOut(ctx, "signed", apkFilename) 496 var lineageFile android.Path 497 if lineage := String(a.properties.Lineage); lineage != "" { 498 lineageFile = android.PathForModuleSrc(ctx, lineage) 499 } 500 501 rotationMinSdkVersion := String(a.properties.RotationMinSdkVersion) 502 503 SignAppPackage(ctx, signed, jnisUncompressed, certificates, nil, lineageFile, rotationMinSdkVersion) 504 a.outputFile = signed 505 } else { 506 validationStamp := a.validatePresignedApk(ctx, srcApk) 507 alignedApk := android.PathForModuleOut(ctx, "zip-aligned", apkFilename) 508 TransformZipAlign(ctx, alignedApk, jnisUncompressed, []android.Path{validationStamp}) 509 a.outputFile = alignedApk 510 a.certificate = PresignedCertificate 511 } 512 513 if a.properties.Compress_apk.GetOrDefault(ctx, false) { 514 outputFile := android.PathForModuleOut(ctx, "compressed_apk", apkFilename) 515 ctx.Build(pctx, android.BuildParams{ 516 Rule: gzipRule, 517 Input: a.outputFile, 518 Output: outputFile, 519 Description: "Compressing " + a.outputFile.Base(), 520 }) 521 a.outputFile = outputFile 522 } 523 524 if apexInfo.IsForPlatform() { 525 a.installPath = ctx.InstallFile(installDir, apkFilename, a.outputFile) 526 artifactPath := android.PathForModuleSrc(ctx, a.properties.Apk.GetOrDefault(ctx, "")) 527 a.provenanceMetaDataFile = provenance.GenerateArtifactProvenanceMetaData(ctx, artifactPath, a.installPath) 528 } 529 530 providePrebuiltInfo(ctx, 531 prebuiltInfoProps{ 532 baseModuleName: a.BaseModuleName(), 533 isPrebuilt: true, 534 prebuiltInfo: a.properties.Prebuilt_info, 535 }, 536 ) 537 538 ctx.SetOutputFiles([]android.Path{a.outputFile}, "") 539 540 buildComplianceMetadata(ctx) 541 542 // TODO: androidmk converter jni libs 543} 544 545func (a *AndroidAppImport) validatePresignedApk(ctx android.ModuleContext, srcApk android.Path) android.Path { 546 stamp := android.PathForModuleOut(ctx, "validated-prebuilt", "check.stamp") 547 var extraArgs []string 548 if a.Privileged() { 549 extraArgs = append(extraArgs, "--privileged") 550 if ctx.Config().UncompressPrivAppDex() { 551 extraArgs = append(extraArgs, "--uncompress-priv-app-dex") 552 } 553 } 554 if proptools.Bool(a.properties.Skip_preprocessed_apk_checks) { 555 extraArgs = append(extraArgs, "--skip-preprocessed-apk-checks") 556 } 557 if proptools.Bool(a.properties.Preprocessed) { 558 extraArgs = append(extraArgs, "--preprocessed") 559 } 560 561 ctx.Build(pctx, android.BuildParams{ 562 Rule: checkPresignedApkRule, 563 Input: srcApk, 564 Output: stamp, 565 Args: map[string]string{ 566 "extraArgs": strings.Join(extraArgs, " "), 567 }, 568 }) 569 return stamp 570} 571 572func (a *AndroidAppImport) Prebuilt() *android.Prebuilt { 573 return &a.prebuilt 574} 575 576func (a *AndroidAppImport) Name() string { 577 return a.prebuilt.Name(a.ModuleBase.Name()) 578} 579 580func (a *AndroidAppImport) OutputFile() android.Path { 581 return a.outputFile 582} 583 584func (a *AndroidAppImport) JacocoReportClassesFile() android.Path { 585 return nil 586} 587 588func (a *AndroidAppImport) Certificate() Certificate { 589 return a.certificate 590} 591 592func (a *AndroidAppImport) ProvenanceMetaDataFile() android.Path { 593 return a.provenanceMetaDataFile 594} 595 596func (a *AndroidAppImport) PrivAppAllowlist() android.OptionalPath { 597 return android.OptionalPath{} 598} 599 600const ( 601 ArchGroupName = "Arch" 602 DpiGroupName = "Dpi_variants" 603) 604 605var dpiVariantGroupType reflect.Type 606var archVariantGroupType reflect.Type 607var archdpiVariantGroupType reflect.Type 608var supportedDpis = []string{"ldpi", "mdpi", "hdpi", "xhdpi", "xxhdpi", "xxxhdpi"} 609 610func initAndroidAppImportVariantGroupTypes() { 611 dpiVariantGroupType = createVariantGroupType(supportedDpis, DpiGroupName) 612 613 archNames := make([]string, len(android.ArchTypeList())) 614 for i, archType := range android.ArchTypeList() { 615 archNames[i] = archType.Name 616 } 617 archVariantGroupType = createVariantGroupType(archNames, ArchGroupName) 618 archdpiVariantGroupType = createArchDpiVariantGroupType(archNames, supportedDpis) 619} 620 621// Populates all variant struct properties at creation time. 622func (a *AndroidAppImport) populateAllVariantStructs() { 623 a.dpiVariants = reflect.New(dpiVariantGroupType).Interface() 624 a.AddProperties(a.dpiVariants) 625 626 a.archVariants = reflect.New(archVariantGroupType).Interface() 627 a.AddProperties(a.archVariants) 628 629 a.arch_dpiVariants = reflect.New(archdpiVariantGroupType).Interface() 630 a.AddProperties(a.arch_dpiVariants) 631} 632 633func (a *AndroidAppImport) Privileged() bool { 634 return Bool(a.properties.Privileged) 635} 636 637func (m *AndroidAppImport) GetDepInSameApexChecker() android.DepInSameApexChecker { 638 return AppImportDepInSameApexChecker{} 639} 640 641type AppImportDepInSameApexChecker struct { 642 android.BaseDepInSameApexChecker 643} 644 645func (m AppImportDepInSameApexChecker) OutgoingDepIsInSameApex(tag blueprint.DependencyTag) bool { 646 // android_app_import might have extra dependencies via uses_libs property. 647 // Don't track the dependency as we don't automatically add those libraries 648 // to the classpath. It should be explicitly added to java_libs property of APEX 649 return false 650} 651 652func (a *AndroidAppImport) SdkVersion(ctx android.EarlyModuleContext) android.SdkSpec { 653 return android.SdkSpecPrivate 654} 655 656func (a *AndroidAppImport) MinSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel { 657 return android.SdkSpecPrivate.ApiLevel 658} 659 660var _ android.ApexModule = (*AndroidAppImport)(nil) 661 662// Implements android.ApexModule 663func (m *AndroidAppImport) MinSdkVersionSupported(ctx android.BaseModuleContext) android.ApiLevel { 664 return android.MinApiLevel 665} 666 667func createVariantGroupType(variants []string, variantGroupName string) reflect.Type { 668 props := reflect.TypeOf((*AndroidAppImportProperties)(nil)) 669 670 variantFields := make([]reflect.StructField, len(variants)) 671 for i, variant := range variants { 672 variantFields[i] = reflect.StructField{ 673 Name: proptools.FieldNameForProperty(variant), 674 Type: props, 675 } 676 } 677 678 variantGroupStruct := reflect.StructOf(variantFields) 679 return reflect.StructOf([]reflect.StructField{ 680 { 681 Name: variantGroupName, 682 Type: variantGroupStruct, 683 }, 684 }) 685} 686 687func createArchDpiVariantGroupType(archNames []string, dpiNames []string) reflect.Type { 688 props := reflect.TypeOf((*AndroidAppImportProperties)(nil)) 689 690 dpiVariantFields := make([]reflect.StructField, len(dpiNames)) 691 for i, variant_dpi := range dpiNames { 692 dpiVariantFields[i] = reflect.StructField{ 693 Name: proptools.FieldNameForProperty(variant_dpi), 694 Type: props, 695 } 696 } 697 dpiVariantGroupStruct := reflect.StructOf(dpiVariantFields) 698 dpi_struct := reflect.StructOf([]reflect.StructField{ 699 { 700 Name: DpiGroupName, 701 Type: reflect.PointerTo(dpiVariantGroupStruct), 702 }, 703 }) 704 705 archVariantFields := make([]reflect.StructField, len(archNames)) 706 for i, variant_arch := range archNames { 707 archVariantFields[i] = reflect.StructField{ 708 Name: proptools.FieldNameForProperty(variant_arch), 709 Type: reflect.PointerTo(dpi_struct), 710 } 711 } 712 archVariantGroupStruct := reflect.StructOf(archVariantFields) 713 714 return_struct := reflect.StructOf([]reflect.StructField{ 715 { 716 Name: ArchGroupName, 717 Type: reflect.PointerTo(archVariantGroupStruct), 718 }, 719 }) 720 return return_struct 721} 722 723func (a *AndroidAppImport) UsesLibrary() *usesLibrary { 724 return &a.usesLibrary 725} 726 727var _ ModuleWithUsesLibrary = (*AndroidAppImport)(nil) 728 729// android_app_import imports a prebuilt apk with additional processing specified in the module. 730// DPI-specific apk source files can be specified using dpi_variants. Example: 731// 732// android_app_import { 733// name: "example_import", 734// apk: "prebuilts/example.apk", 735// dpi_variants: { 736// mdpi: { 737// apk: "prebuilts/example_mdpi.apk", 738// }, 739// xhdpi: { 740// apk: "prebuilts/example_xhdpi.apk", 741// }, 742// }, 743// presigned: true, 744// } 745func AndroidAppImportFactory() android.Module { 746 module := &AndroidAppImport{} 747 module.AddProperties(&module.properties) 748 module.AddProperties(&module.dexpreoptProperties) 749 module.AddProperties(&module.usesLibrary.usesLibraryProperties) 750 module.populateAllVariantStructs() 751 module.SetDefaultableHook(func(ctx android.DefaultableHookContext) { 752 module.processVariants(ctx) 753 }) 754 755 android.InitApexModule(module) 756 android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon) 757 android.InitDefaultableModule(module) 758 android.InitConfigurablePrebuiltModuleString(module, &module.properties.Apk, "Apk") 759 760 module.usesLibrary.enforce = true 761 762 return module 763} 764 765type AndroidTestImport struct { 766 AndroidAppImport 767 768 testProperties struct { 769 // list of compatibility suites (for example "cts", "vts") that the module should be 770 // installed into. 771 Test_suites []string `android:"arch_variant"` 772 773 // list of files or filegroup modules that provide data that should be installed alongside 774 // the test 775 Data []string `android:"path"` 776 777 // Install the test into a folder named for the module in all test suites. 778 Per_testcase_directory *bool 779 } 780 781 data android.Paths 782} 783 784func (a *AndroidTestImport) GenerateAndroidBuildActions(ctx android.ModuleContext) { 785 a.generateAndroidBuildActions(ctx) 786 787 a.updateModuleInfoJSON(ctx) 788 789 a.data = android.PathsForModuleSrc(ctx, a.testProperties.Data) 790 791 android.SetProvider(ctx, android.TestSuiteInfoProvider, android.TestSuiteInfo{ 792 TestSuites: a.testProperties.Test_suites, 793 }) 794} 795 796func (a *AndroidTestImport) updateModuleInfoJSON(ctx android.ModuleContext) { 797 moduleInfoJSON := ctx.ModuleInfoJSON() 798 moduleInfoJSON.Class = []string{"APPS"} 799 moduleInfoJSON.CompatibilitySuites = []string{"null-suite"} 800 if len(a.testProperties.Test_suites) > 0 { 801 moduleInfoJSON.CompatibilitySuites = a.testProperties.Test_suites 802 } 803 moduleInfoJSON.SystemSharedLibs = []string{"none"} 804 moduleInfoJSON.Tags = []string{"tests"} 805 moduleInfoJSON.RegisterNameOverride = a.BaseModuleName() 806 testConfig := android.ExistentPathForSource(ctx, ctx.ModuleDir(), "AndroidTest.xml") 807 if testConfig.Valid() { 808 moduleInfoJSON.TestConfig = []string{testConfig.String()} 809 } 810} 811 812func (a *AndroidTestImport) InstallInTestcases() bool { 813 return true 814} 815 816// android_test_import imports a prebuilt test apk with additional processing specified in the 817// module. DPI or arch variant configurations can be made as with android_app_import. 818func AndroidTestImportFactory() android.Module { 819 module := &AndroidTestImport{} 820 module.AddProperties(&module.properties) 821 module.AddProperties(&module.dexpreoptProperties) 822 module.AddProperties(&module.testProperties) 823 module.populateAllVariantStructs() 824 module.SetDefaultableHook(func(ctx android.DefaultableHookContext) { 825 module.processVariants(ctx) 826 }) 827 828 module.dexpreopter.isTest = true 829 830 android.InitApexModule(module) 831 android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon) 832 android.InitDefaultableModule(module) 833 android.InitConfigurablePrebuiltModuleString(module, &module.properties.Apk, "Apk") 834 835 return module 836} 837