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 22 "github.com/google/blueprint/proptools" 23 24 "android/soong/android" 25 "android/soong/provenance" 26) 27 28func init() { 29 RegisterAppImportBuildComponents(android.InitRegistrationContext) 30 31 initAndroidAppImportVariantGroupTypes() 32} 33 34func RegisterAppImportBuildComponents(ctx android.RegistrationContext) { 35 ctx.RegisterModuleType("android_app_import", AndroidAppImportFactory) 36 ctx.RegisterModuleType("android_test_import", AndroidTestImportFactory) 37} 38 39type AndroidAppImport struct { 40 android.ModuleBase 41 android.DefaultableModuleBase 42 android.ApexModuleBase 43 prebuilt android.Prebuilt 44 45 properties AndroidAppImportProperties 46 dpiVariants interface{} 47 archVariants interface{} 48 49 outputFile android.Path 50 certificate Certificate 51 52 dexpreopter 53 54 usesLibrary usesLibrary 55 56 preprocessed bool 57 58 installPath android.InstallPath 59 60 hideApexVariantFromMake bool 61 62 provenanceMetaDataFile android.OutputPath 63} 64 65type AndroidAppImportProperties struct { 66 // A prebuilt apk to import 67 Apk *string `android:"path"` 68 69 // The name of a certificate in the default certificate directory or an android_app_certificate 70 // module name in the form ":module". Should be empty if presigned or default_dev_cert is set. 71 Certificate *string 72 73 // Names of extra android_app_certificate modules to sign the apk with in the form ":module". 74 Additional_certificates []string 75 76 // Set this flag to true if the prebuilt apk is already signed. The certificate property must not 77 // be set for presigned modules. 78 Presigned *bool 79 80 // Name of the signing certificate lineage file or filegroup module. 81 Lineage *string `android:"path"` 82 83 // For overriding the --rotation-min-sdk-version property of apksig 84 RotationMinSdkVersion *string 85 86 // Sign with the default system dev certificate. Must be used judiciously. Most imported apps 87 // need to either specify a specific certificate or be presigned. 88 Default_dev_cert *bool 89 90 // Specifies that this app should be installed to the priv-app directory, 91 // where the system will grant it additional privileges not available to 92 // normal apps. 93 Privileged *bool 94 95 // Names of modules to be overridden. Listed modules can only be other binaries 96 // (in Make or Soong). 97 // This does not completely prevent installation of the overridden binaries, but if both 98 // binaries would be installed by default (in PRODUCT_PACKAGES) the other binary will be removed 99 // from PRODUCT_PACKAGES. 100 Overrides []string 101 102 // Optional name for the installed app. If unspecified, it is derived from the module name. 103 Filename *string 104 105 // If set, create package-export.apk, which other packages can 106 // use to get PRODUCT-agnostic resource data like IDs and type definitions. 107 Export_package_resources *bool 108 109 // Optional. Install to a subdirectory of the default install path for the module 110 Relative_install_path *string 111} 112 113func (a *AndroidAppImport) IsInstallable() bool { 114 return true 115} 116 117// Updates properties with variant-specific values. 118func (a *AndroidAppImport) processVariants(ctx android.LoadHookContext) { 119 config := ctx.Config() 120 121 dpiProps := reflect.ValueOf(a.dpiVariants).Elem().FieldByName("Dpi_variants") 122 // Try DPI variant matches in the reverse-priority order so that the highest priority match 123 // overwrites everything else. 124 // TODO(jungjw): Can we optimize this by making it priority order? 125 for i := len(config.ProductAAPTPrebuiltDPI()) - 1; i >= 0; i-- { 126 MergePropertiesFromVariant(ctx, &a.properties, dpiProps, config.ProductAAPTPrebuiltDPI()[i]) 127 } 128 if config.ProductAAPTPreferredConfig() != "" { 129 MergePropertiesFromVariant(ctx, &a.properties, dpiProps, config.ProductAAPTPreferredConfig()) 130 } 131 132 archProps := reflect.ValueOf(a.archVariants).Elem().FieldByName("Arch") 133 archType := ctx.Config().AndroidFirstDeviceTarget.Arch.ArchType 134 MergePropertiesFromVariant(ctx, &a.properties, archProps, archType.Name) 135 136 if String(a.properties.Apk) == "" { 137 // Disable this module since the apk property is still empty after processing all matching 138 // variants. This likely means there is no matching variant, and the default variant doesn't 139 // have an apk property value either. 140 a.Disable() 141 } 142} 143 144func MergePropertiesFromVariant(ctx android.EarlyModuleContext, 145 dst interface{}, variantGroup reflect.Value, variant string) { 146 src := variantGroup.FieldByName(proptools.FieldNameForProperty(variant)) 147 if !src.IsValid() { 148 return 149 } 150 151 err := proptools.ExtendMatchingProperties([]interface{}{dst}, src.Interface(), nil, proptools.OrderAppend) 152 if err != nil { 153 if propertyErr, ok := err.(*proptools.ExtendPropertyError); ok { 154 ctx.PropertyErrorf(propertyErr.Property, "%s", propertyErr.Err.Error()) 155 } else { 156 panic(err) 157 } 158 } 159} 160 161func (a *AndroidAppImport) isPrebuiltFrameworkRes() bool { 162 return a.Name() == "prebuilt_framework-res" 163} 164 165func (a *AndroidAppImport) DepsMutator(ctx android.BottomUpMutatorContext) { 166 cert := android.SrcIsModule(String(a.properties.Certificate)) 167 if cert != "" { 168 ctx.AddDependency(ctx.Module(), certificateTag, cert) 169 } 170 171 for _, cert := range a.properties.Additional_certificates { 172 cert = android.SrcIsModule(cert) 173 if cert != "" { 174 ctx.AddDependency(ctx.Module(), certificateTag, cert) 175 } else { 176 ctx.PropertyErrorf("additional_certificates", 177 `must be names of android_app_certificate modules in the form ":module"`) 178 } 179 } 180 181 a.usesLibrary.deps(ctx, !a.isPrebuiltFrameworkRes()) 182} 183 184func (a *AndroidAppImport) uncompressEmbeddedJniLibs( 185 ctx android.ModuleContext, inputPath android.Path, outputPath android.OutputPath) { 186 // Test apps don't need their JNI libraries stored uncompressed. As a matter of fact, messing 187 // with them may invalidate pre-existing signature data. 188 if ctx.InstallInTestcases() && (Bool(a.properties.Presigned) || a.preprocessed) { 189 ctx.Build(pctx, android.BuildParams{ 190 Rule: android.Cp, 191 Output: outputPath, 192 Input: inputPath, 193 }) 194 return 195 } 196 rule := android.NewRuleBuilder(pctx, ctx) 197 rule.Command(). 198 Textf(`if (zipinfo %s 'lib/*.so' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then`, inputPath). 199 BuiltTool("zip2zip"). 200 FlagWithInput("-i ", inputPath). 201 FlagWithOutput("-o ", outputPath). 202 FlagWithArg("-0 ", "'lib/**/*.so'"). 203 Textf(`; else cp -f %s %s; fi`, inputPath, outputPath) 204 rule.Build("uncompress-embedded-jni-libs", "Uncompress embedded JIN libs") 205} 206 207// Returns whether this module should have the dex file stored uncompressed in the APK. 208func (a *AndroidAppImport) shouldUncompressDex(ctx android.ModuleContext) bool { 209 if ctx.Config().UnbundledBuild() || a.preprocessed { 210 return false 211 } 212 213 // Uncompress dex in APKs of priv-apps if and only if DONT_UNCOMPRESS_PRIV_APPS_DEXS is false. 214 if a.Privileged() { 215 return ctx.Config().UncompressPrivAppDex() 216 } 217 218 return shouldUncompressDex(ctx, &a.dexpreopter) 219} 220 221func (a *AndroidAppImport) uncompressDex( 222 ctx android.ModuleContext, inputPath android.Path, outputPath android.OutputPath) { 223 rule := android.NewRuleBuilder(pctx, ctx) 224 rule.Command(). 225 Textf(`if (zipinfo %s '*.dex' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then`, inputPath). 226 BuiltTool("zip2zip"). 227 FlagWithInput("-i ", inputPath). 228 FlagWithOutput("-o ", outputPath). 229 FlagWithArg("-0 ", "'classes*.dex'"). 230 Textf(`; else cp -f %s %s; fi`, inputPath, outputPath) 231 rule.Build("uncompress-dex", "Uncompress dex files") 232} 233 234func (a *AndroidAppImport) GenerateAndroidBuildActions(ctx android.ModuleContext) { 235 a.generateAndroidBuildActions(ctx) 236} 237 238func (a *AndroidAppImport) InstallApkName() string { 239 return a.BaseModuleName() 240} 241 242func (a *AndroidAppImport) generateAndroidBuildActions(ctx android.ModuleContext) { 243 apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo) 244 if !apexInfo.IsForPlatform() { 245 a.hideApexVariantFromMake = true 246 } 247 248 numCertPropsSet := 0 249 if String(a.properties.Certificate) != "" { 250 numCertPropsSet++ 251 } 252 if Bool(a.properties.Presigned) { 253 numCertPropsSet++ 254 } 255 if Bool(a.properties.Default_dev_cert) { 256 numCertPropsSet++ 257 } 258 if numCertPropsSet != 1 { 259 ctx.ModuleErrorf("One and only one of certficate, presigned, and default_dev_cert properties must be set") 260 } 261 262 _, certificates := collectAppDeps(ctx, a, false, false) 263 264 // TODO: LOCAL_EXTRACT_APK/LOCAL_EXTRACT_DPI_APK 265 // TODO: LOCAL_PACKAGE_SPLITS 266 267 srcApk := a.prebuilt.SingleSourcePath(ctx) 268 269 // TODO: Install or embed JNI libraries 270 271 // Uncompress JNI libraries in the apk 272 jnisUncompressed := android.PathForModuleOut(ctx, "jnis-uncompressed", ctx.ModuleName()+".apk") 273 a.uncompressEmbeddedJniLibs(ctx, srcApk, jnisUncompressed.OutputPath) 274 275 var pathFragments []string 276 relInstallPath := String(a.properties.Relative_install_path) 277 278 if a.isPrebuiltFrameworkRes() { 279 // framework-res.apk is installed as system/framework/framework-res.apk 280 if relInstallPath != "" { 281 ctx.PropertyErrorf("relative_install_path", "Relative_install_path cannot be set for framework-res") 282 } 283 pathFragments = []string{"framework"} 284 a.preprocessed = true 285 } else if Bool(a.properties.Privileged) { 286 pathFragments = []string{"priv-app", relInstallPath, a.BaseModuleName()} 287 } else if ctx.InstallInTestcases() { 288 pathFragments = []string{relInstallPath, a.BaseModuleName(), ctx.DeviceConfig().DeviceArch()} 289 } else { 290 pathFragments = []string{"app", relInstallPath, a.BaseModuleName()} 291 } 292 293 installDir := android.PathForModuleInstall(ctx, pathFragments...) 294 a.dexpreopter.isApp = true 295 a.dexpreopter.installPath = installDir.Join(ctx, a.BaseModuleName()+".apk") 296 a.dexpreopter.isPresignedPrebuilt = Bool(a.properties.Presigned) 297 a.dexpreopter.uncompressedDex = a.shouldUncompressDex(ctx) 298 299 a.dexpreopter.enforceUsesLibs = a.usesLibrary.enforceUsesLibraries() 300 a.dexpreopter.classLoaderContexts = a.usesLibrary.classLoaderContextForUsesLibDeps(ctx) 301 302 if a.usesLibrary.enforceUsesLibraries() { 303 srcApk = a.usesLibrary.verifyUsesLibrariesAPK(ctx, srcApk) 304 } 305 306 a.dexpreopter.dexpreopt(ctx, jnisUncompressed) 307 if a.dexpreopter.uncompressedDex { 308 dexUncompressed := android.PathForModuleOut(ctx, "dex-uncompressed", ctx.ModuleName()+".apk") 309 a.uncompressDex(ctx, jnisUncompressed, dexUncompressed.OutputPath) 310 jnisUncompressed = dexUncompressed 311 } 312 313 apkFilename := proptools.StringDefault(a.properties.Filename, a.BaseModuleName()+".apk") 314 315 // TODO: Handle EXTERNAL 316 317 // Sign or align the package if package has not been preprocessed 318 319 if a.isPrebuiltFrameworkRes() { 320 a.outputFile = srcApk 321 certificates = processMainCert(a.ModuleBase, String(a.properties.Certificate), certificates, ctx) 322 if len(certificates) != 1 { 323 ctx.ModuleErrorf("Unexpected number of certificates were extracted: %q", certificates) 324 } 325 a.certificate = certificates[0] 326 } else if a.preprocessed { 327 a.outputFile = srcApk 328 a.certificate = PresignedCertificate 329 } else if !Bool(a.properties.Presigned) { 330 // If the certificate property is empty at this point, default_dev_cert must be set to true. 331 // Which makes processMainCert's behavior for the empty cert string WAI. 332 certificates = processMainCert(a.ModuleBase, String(a.properties.Certificate), certificates, ctx) 333 a.certificate = certificates[0] 334 signed := android.PathForModuleOut(ctx, "signed", apkFilename) 335 var lineageFile android.Path 336 if lineage := String(a.properties.Lineage); lineage != "" { 337 lineageFile = android.PathForModuleSrc(ctx, lineage) 338 } 339 340 rotationMinSdkVersion := String(a.properties.RotationMinSdkVersion) 341 342 SignAppPackage(ctx, signed, jnisUncompressed, certificates, nil, lineageFile, rotationMinSdkVersion) 343 a.outputFile = signed 344 } else { 345 alignedApk := android.PathForModuleOut(ctx, "zip-aligned", apkFilename) 346 TransformZipAlign(ctx, alignedApk, jnisUncompressed) 347 a.outputFile = alignedApk 348 a.certificate = PresignedCertificate 349 } 350 351 // TODO: Optionally compress the output apk. 352 353 if apexInfo.IsForPlatform() { 354 a.installPath = ctx.InstallFile(installDir, apkFilename, a.outputFile) 355 artifactPath := android.PathForModuleSrc(ctx, *a.properties.Apk) 356 a.provenanceMetaDataFile = provenance.GenerateArtifactProvenanceMetaData(ctx, artifactPath, a.installPath) 357 } 358 359 // TODO: androidmk converter jni libs 360} 361 362func (a *AndroidAppImport) Prebuilt() *android.Prebuilt { 363 return &a.prebuilt 364} 365 366func (a *AndroidAppImport) Name() string { 367 return a.prebuilt.Name(a.ModuleBase.Name()) 368} 369 370func (a *AndroidAppImport) OutputFile() android.Path { 371 return a.outputFile 372} 373 374func (a *AndroidAppImport) JacocoReportClassesFile() android.Path { 375 return nil 376} 377 378func (a *AndroidAppImport) Certificate() Certificate { 379 return a.certificate 380} 381 382func (a *AndroidAppImport) ProvenanceMetaDataFile() android.OutputPath { 383 return a.provenanceMetaDataFile 384} 385 386var dpiVariantGroupType reflect.Type 387var archVariantGroupType reflect.Type 388var supportedDpis = []string{"ldpi", "mdpi", "hdpi", "xhdpi", "xxhdpi", "xxxhdpi"} 389 390func initAndroidAppImportVariantGroupTypes() { 391 dpiVariantGroupType = createVariantGroupType(supportedDpis, "Dpi_variants") 392 393 archNames := make([]string, len(android.ArchTypeList())) 394 for i, archType := range android.ArchTypeList() { 395 archNames[i] = archType.Name 396 } 397 archVariantGroupType = createVariantGroupType(archNames, "Arch") 398} 399 400// Populates all variant struct properties at creation time. 401func (a *AndroidAppImport) populateAllVariantStructs() { 402 a.dpiVariants = reflect.New(dpiVariantGroupType).Interface() 403 a.AddProperties(a.dpiVariants) 404 405 a.archVariants = reflect.New(archVariantGroupType).Interface() 406 a.AddProperties(a.archVariants) 407} 408 409func (a *AndroidAppImport) Privileged() bool { 410 return Bool(a.properties.Privileged) 411} 412 413func (a *AndroidAppImport) DepIsInSameApex(_ android.BaseModuleContext, _ android.Module) bool { 414 // android_app_import might have extra dependencies via uses_libs property. 415 // Don't track the dependency as we don't automatically add those libraries 416 // to the classpath. It should be explicitly added to java_libs property of APEX 417 return false 418} 419 420func (a *AndroidAppImport) SdkVersion(ctx android.EarlyModuleContext) android.SdkSpec { 421 return android.SdkSpecPrivate 422} 423 424func (a *AndroidAppImport) MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec { 425 return android.SdkSpecPrivate 426} 427 428func (a *AndroidAppImport) LintDepSets() LintDepSets { 429 return LintDepSets{} 430} 431 432var _ android.ApexModule = (*AndroidAppImport)(nil) 433 434// Implements android.ApexModule 435func (j *AndroidAppImport) ShouldSupportSdkVersion(ctx android.BaseModuleContext, 436 sdkVersion android.ApiLevel) error { 437 // Do not check for prebuilts against the min_sdk_version of enclosing APEX 438 return nil 439} 440 441func createVariantGroupType(variants []string, variantGroupName string) reflect.Type { 442 props := reflect.TypeOf((*AndroidAppImportProperties)(nil)) 443 444 variantFields := make([]reflect.StructField, len(variants)) 445 for i, variant := range variants { 446 variantFields[i] = reflect.StructField{ 447 Name: proptools.FieldNameForProperty(variant), 448 Type: props, 449 } 450 } 451 452 variantGroupStruct := reflect.StructOf(variantFields) 453 return reflect.StructOf([]reflect.StructField{ 454 { 455 Name: variantGroupName, 456 Type: variantGroupStruct, 457 }, 458 }) 459} 460 461// android_app_import imports a prebuilt apk with additional processing specified in the module. 462// DPI-specific apk source files can be specified using dpi_variants. Example: 463// 464// android_app_import { 465// name: "example_import", 466// apk: "prebuilts/example.apk", 467// dpi_variants: { 468// mdpi: { 469// apk: "prebuilts/example_mdpi.apk", 470// }, 471// xhdpi: { 472// apk: "prebuilts/example_xhdpi.apk", 473// }, 474// }, 475// presigned: true, 476// } 477func AndroidAppImportFactory() android.Module { 478 module := &AndroidAppImport{} 479 module.AddProperties(&module.properties) 480 module.AddProperties(&module.dexpreoptProperties) 481 module.AddProperties(&module.usesLibrary.usesLibraryProperties) 482 module.populateAllVariantStructs() 483 android.AddLoadHook(module, func(ctx android.LoadHookContext) { 484 module.processVariants(ctx) 485 }) 486 487 android.InitApexModule(module) 488 android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon) 489 android.InitDefaultableModule(module) 490 android.InitSingleSourcePrebuiltModule(module, &module.properties, "Apk") 491 492 module.usesLibrary.enforce = true 493 494 return module 495} 496 497type androidTestImportProperties struct { 498 // Whether the prebuilt apk can be installed without additional processing. Default is false. 499 Preprocessed *bool 500} 501 502type AndroidTestImport struct { 503 AndroidAppImport 504 505 testProperties testProperties 506 507 testImportProperties androidTestImportProperties 508 509 data android.Paths 510} 511 512func (a *AndroidTestImport) GenerateAndroidBuildActions(ctx android.ModuleContext) { 513 a.preprocessed = Bool(a.testImportProperties.Preprocessed) 514 515 a.generateAndroidBuildActions(ctx) 516 517 a.data = android.PathsForModuleSrc(ctx, a.testProperties.Data) 518} 519 520func (a *AndroidTestImport) InstallInTestcases() bool { 521 return true 522} 523 524// android_test_import imports a prebuilt test apk with additional processing specified in the 525// module. DPI or arch variant configurations can be made as with android_app_import. 526func AndroidTestImportFactory() android.Module { 527 module := &AndroidTestImport{} 528 module.AddProperties(&module.properties) 529 module.AddProperties(&module.dexpreoptProperties) 530 module.AddProperties(&module.testProperties) 531 module.AddProperties(&module.testImportProperties) 532 module.populateAllVariantStructs() 533 android.AddLoadHook(module, func(ctx android.LoadHookContext) { 534 module.processVariants(ctx) 535 }) 536 537 module.dexpreopter.isTest = true 538 539 android.InitApexModule(module) 540 android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon) 541 android.InitDefaultableModule(module) 542 android.InitSingleSourcePrebuiltModule(module, &module.properties, "Apk") 543 544 return module 545} 546