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