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