blob: 5a87b074b601e174fa05df3ded6376d7ab7f810d [file] [log] [blame]
Jaewoong Jungf9b44652020-12-21 12:29:12 -08001// 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
Jooyung Hanf05ca9c2021-06-28 21:48:51 +090064 Apk *string `android:"path"`
Jaewoong Jungf9b44652020-12-21 12:29:12 -080065
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
Jaewoong Jung25ae8de2021-03-08 17:37:46 -080070 // Names of extra android_app_certificate modules to sign the apk with in the form ":module".
71 Additional_certificates []string
72
Jaewoong Jungf9b44652020-12-21 12:29:12 -080073 // 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
Jaewoong Jung1c1b6e62021-03-09 15:02:31 -080077 // Name of the signing certificate lineage file or filegroup module.
78 Lineage *string `android:"path"`
Jaewoong Jungf9b44652020-12-21 12:29:12 -080079
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
Bill Peckhama036da92021-01-08 16:09:09 -080098
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
Spandan Dasd1fac642021-05-18 17:01:41 +0000102
103 // Optional. Install to a subdirectory of the default install path for the module
104 Relative_install_path *string
Jaewoong Jungf9b44652020-12-21 12:29:12 -0800105}
106
107func (a *AndroidAppImport) IsInstallable() bool {
108 return true
109}
110
111// Updates properties with variant-specific values.
112func (a *AndroidAppImport) processVariants(ctx android.LoadHookContext) {
113 config := ctx.Config()
114
115 dpiProps := reflect.ValueOf(a.dpiVariants).Elem().FieldByName("Dpi_variants")
116 // Try DPI variant matches in the reverse-priority order so that the highest priority match
117 // overwrites everything else.
118 // TODO(jungjw): Can we optimize this by making it priority order?
119 for i := len(config.ProductAAPTPrebuiltDPI()) - 1; i >= 0; i-- {
120 MergePropertiesFromVariant(ctx, &a.properties, dpiProps, config.ProductAAPTPrebuiltDPI()[i])
121 }
122 if config.ProductAAPTPreferredConfig() != "" {
123 MergePropertiesFromVariant(ctx, &a.properties, dpiProps, config.ProductAAPTPreferredConfig())
124 }
125
126 archProps := reflect.ValueOf(a.archVariants).Elem().FieldByName("Arch")
127 archType := ctx.Config().AndroidFirstDeviceTarget.Arch.ArchType
128 MergePropertiesFromVariant(ctx, &a.properties, archProps, archType.Name)
129
130 if String(a.properties.Apk) == "" {
131 // Disable this module since the apk property is still empty after processing all matching
132 // variants. This likely means there is no matching variant, and the default variant doesn't
133 // have an apk property value either.
134 a.Disable()
135 }
136}
137
138func MergePropertiesFromVariant(ctx android.EarlyModuleContext,
139 dst interface{}, variantGroup reflect.Value, variant string) {
140 src := variantGroup.FieldByName(proptools.FieldNameForProperty(variant))
141 if !src.IsValid() {
142 return
143 }
144
145 err := proptools.ExtendMatchingProperties([]interface{}{dst}, src.Interface(), nil, proptools.OrderAppend)
146 if err != nil {
147 if propertyErr, ok := err.(*proptools.ExtendPropertyError); ok {
148 ctx.PropertyErrorf(propertyErr.Property, "%s", propertyErr.Err.Error())
149 } else {
150 panic(err)
151 }
152 }
153}
154
Bill Peckhama036da92021-01-08 16:09:09 -0800155func (a *AndroidAppImport) isPrebuiltFrameworkRes() bool {
156 return a.Name() == "prebuilt_framework-res"
157}
158
Jaewoong Jungf9b44652020-12-21 12:29:12 -0800159func (a *AndroidAppImport) DepsMutator(ctx android.BottomUpMutatorContext) {
160 cert := android.SrcIsModule(String(a.properties.Certificate))
161 if cert != "" {
162 ctx.AddDependency(ctx.Module(), certificateTag, cert)
163 }
164
Jaewoong Jung25ae8de2021-03-08 17:37:46 -0800165 for _, cert := range a.properties.Additional_certificates {
166 cert = android.SrcIsModule(cert)
167 if cert != "" {
168 ctx.AddDependency(ctx.Module(), certificateTag, cert)
169 } else {
170 ctx.PropertyErrorf("additional_certificates",
171 `must be names of android_app_certificate modules in the form ":module"`)
172 }
173 }
174
Bill Peckhama036da92021-01-08 16:09:09 -0800175 a.usesLibrary.deps(ctx, !a.isPrebuiltFrameworkRes())
Jaewoong Jungf9b44652020-12-21 12:29:12 -0800176}
177
178func (a *AndroidAppImport) uncompressEmbeddedJniLibs(
179 ctx android.ModuleContext, inputPath android.Path, outputPath android.OutputPath) {
180 // Test apps don't need their JNI libraries stored uncompressed. As a matter of fact, messing
181 // with them may invalidate pre-existing signature data.
182 if ctx.InstallInTestcases() && (Bool(a.properties.Presigned) || a.preprocessed) {
183 ctx.Build(pctx, android.BuildParams{
184 Rule: android.Cp,
185 Output: outputPath,
186 Input: inputPath,
187 })
188 return
189 }
190 rule := android.NewRuleBuilder(pctx, ctx)
191 rule.Command().
192 Textf(`if (zipinfo %s 'lib/*.so' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then`, inputPath).
193 BuiltTool("zip2zip").
194 FlagWithInput("-i ", inputPath).
195 FlagWithOutput("-o ", outputPath).
196 FlagWithArg("-0 ", "'lib/**/*.so'").
197 Textf(`; else cp -f %s %s; fi`, inputPath, outputPath)
198 rule.Build("uncompress-embedded-jni-libs", "Uncompress embedded JIN libs")
199}
200
201// Returns whether this module should have the dex file stored uncompressed in the APK.
202func (a *AndroidAppImport) shouldUncompressDex(ctx android.ModuleContext) bool {
203 if ctx.Config().UnbundledBuild() || a.preprocessed {
204 return false
205 }
206
207 // Uncompress dex in APKs of privileged apps
208 if ctx.Config().UncompressPrivAppDex() && a.Privileged() {
209 return true
210 }
211
212 return shouldUncompressDex(ctx, &a.dexpreopter)
213}
214
215func (a *AndroidAppImport) uncompressDex(
216 ctx android.ModuleContext, inputPath android.Path, outputPath android.OutputPath) {
217 rule := android.NewRuleBuilder(pctx, ctx)
218 rule.Command().
219 Textf(`if (zipinfo %s '*.dex' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then`, inputPath).
220 BuiltTool("zip2zip").
221 FlagWithInput("-i ", inputPath).
222 FlagWithOutput("-o ", outputPath).
223 FlagWithArg("-0 ", "'classes*.dex'").
224 Textf(`; else cp -f %s %s; fi`, inputPath, outputPath)
225 rule.Build("uncompress-dex", "Uncompress dex files")
226}
227
228func (a *AndroidAppImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
229 a.generateAndroidBuildActions(ctx)
230}
231
232func (a *AndroidAppImport) InstallApkName() string {
233 return a.BaseModuleName()
234}
235
236func (a *AndroidAppImport) generateAndroidBuildActions(ctx android.ModuleContext) {
237 apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
238 if !apexInfo.IsForPlatform() {
239 a.hideApexVariantFromMake = true
240 }
241
242 numCertPropsSet := 0
243 if String(a.properties.Certificate) != "" {
244 numCertPropsSet++
245 }
246 if Bool(a.properties.Presigned) {
247 numCertPropsSet++
248 }
249 if Bool(a.properties.Default_dev_cert) {
250 numCertPropsSet++
251 }
252 if numCertPropsSet != 1 {
253 ctx.ModuleErrorf("One and only one of certficate, presigned, and default_dev_cert properties must be set")
254 }
255
256 _, certificates := collectAppDeps(ctx, a, false, false)
257
258 // TODO: LOCAL_EXTRACT_APK/LOCAL_EXTRACT_DPI_APK
259 // TODO: LOCAL_PACKAGE_SPLITS
260
261 srcApk := a.prebuilt.SingleSourcePath(ctx)
262
Jaewoong Jungf9b44652020-12-21 12:29:12 -0800263 // TODO: Install or embed JNI libraries
264
265 // Uncompress JNI libraries in the apk
266 jnisUncompressed := android.PathForModuleOut(ctx, "jnis-uncompressed", ctx.ModuleName()+".apk")
267 a.uncompressEmbeddedJniLibs(ctx, srcApk, jnisUncompressed.OutputPath)
268
Spandan Dasd1fac642021-05-18 17:01:41 +0000269 var pathFragments []string
270 relInstallPath := String(a.properties.Relative_install_path)
Bill Peckhama036da92021-01-08 16:09:09 -0800271
272 if a.isPrebuiltFrameworkRes() {
273 // framework-res.apk is installed as system/framework/framework-res.apk
Spandan Dasd1fac642021-05-18 17:01:41 +0000274 if relInstallPath != "" {
275 ctx.PropertyErrorf("relative_install_path", "Relative_install_path cannot be set for framework-res")
276 }
277 pathFragments = []string{"framework"}
Bill Peckhama036da92021-01-08 16:09:09 -0800278 a.preprocessed = true
279 } else if Bool(a.properties.Privileged) {
Spandan Dasd1fac642021-05-18 17:01:41 +0000280 pathFragments = []string{"priv-app", relInstallPath, a.BaseModuleName()}
Jaewoong Jungf9b44652020-12-21 12:29:12 -0800281 } else if ctx.InstallInTestcases() {
Spandan Dasd1fac642021-05-18 17:01:41 +0000282 pathFragments = []string{relInstallPath, a.BaseModuleName(), ctx.DeviceConfig().DeviceArch()}
Jaewoong Jungf9b44652020-12-21 12:29:12 -0800283 } else {
Spandan Dasd1fac642021-05-18 17:01:41 +0000284 pathFragments = []string{"app", relInstallPath, a.BaseModuleName()}
Jaewoong Jungf9b44652020-12-21 12:29:12 -0800285 }
286
Spandan Dasd1fac642021-05-18 17:01:41 +0000287 installDir := android.PathForModuleInstall(ctx, pathFragments...)
Ulya Trafimovich76b08522021-01-14 17:52:43 +0000288 a.dexpreopter.isApp = true
Jaewoong Jungf9b44652020-12-21 12:29:12 -0800289 a.dexpreopter.installPath = installDir.Join(ctx, a.BaseModuleName()+".apk")
290 a.dexpreopter.isPresignedPrebuilt = Bool(a.properties.Presigned)
291 a.dexpreopter.uncompressedDex = a.shouldUncompressDex(ctx)
292
293 a.dexpreopter.enforceUsesLibs = a.usesLibrary.enforceUsesLibraries()
294 a.dexpreopter.classLoaderContexts = a.usesLibrary.classLoaderContextForUsesLibDeps(ctx)
295
Ulya Trafimovichfe927a22021-02-26 14:36:48 +0000296 if a.usesLibrary.enforceUsesLibraries() {
297 srcApk = a.usesLibrary.verifyUsesLibrariesAPK(ctx, srcApk)
298 }
299
Jaewoong Jungf9b44652020-12-21 12:29:12 -0800300 a.dexpreopter.dexpreopt(ctx, jnisUncompressed)
301 if a.dexpreopter.uncompressedDex {
302 dexUncompressed := android.PathForModuleOut(ctx, "dex-uncompressed", ctx.ModuleName()+".apk")
303 a.uncompressDex(ctx, jnisUncompressed, dexUncompressed.OutputPath)
304 jnisUncompressed = dexUncompressed
305 }
306
307 apkFilename := proptools.StringDefault(a.properties.Filename, a.BaseModuleName()+".apk")
308
309 // TODO: Handle EXTERNAL
310
311 // Sign or align the package if package has not been preprocessed
Bill Peckhama036da92021-01-08 16:09:09 -0800312
313 if a.isPrebuiltFrameworkRes() {
314 a.outputFile = srcApk
315 certificates = processMainCert(a.ModuleBase, String(a.properties.Certificate), certificates, ctx)
316 if len(certificates) != 1 {
317 ctx.ModuleErrorf("Unexpected number of certificates were extracted: %q", certificates)
318 }
319 a.certificate = certificates[0]
320 } else if a.preprocessed {
Jaewoong Jungf9b44652020-12-21 12:29:12 -0800321 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 certificates = processMainCert(a.ModuleBase, String(a.properties.Certificate), certificates, ctx)
Jaewoong Jungf9b44652020-12-21 12:29:12 -0800327 a.certificate = certificates[0]
328 signed := android.PathForModuleOut(ctx, "signed", apkFilename)
329 var lineageFile android.Path
330 if lineage := String(a.properties.Lineage); lineage != "" {
331 lineageFile = android.PathForModuleSrc(ctx, lineage)
332 }
333 SignAppPackage(ctx, signed, jnisUncompressed, certificates, nil, lineageFile)
334 a.outputFile = signed
335 } else {
336 alignedApk := android.PathForModuleOut(ctx, "zip-aligned", apkFilename)
337 TransformZipAlign(ctx, alignedApk, jnisUncompressed)
338 a.outputFile = alignedApk
339 a.certificate = PresignedCertificate
340 }
341
342 // TODO: Optionally compress the output apk.
343
344 if apexInfo.IsForPlatform() {
345 a.installPath = ctx.InstallFile(installDir, apkFilename, a.outputFile)
346 }
347
348 // TODO: androidmk converter jni libs
349}
350
351func (a *AndroidAppImport) Prebuilt() *android.Prebuilt {
352 return &a.prebuilt
353}
354
355func (a *AndroidAppImport) Name() string {
356 return a.prebuilt.Name(a.ModuleBase.Name())
357}
358
359func (a *AndroidAppImport) OutputFile() android.Path {
360 return a.outputFile
361}
362
363func (a *AndroidAppImport) JacocoReportClassesFile() android.Path {
364 return nil
365}
366
367func (a *AndroidAppImport) Certificate() Certificate {
368 return a.certificate
369}
370
371var dpiVariantGroupType reflect.Type
372var archVariantGroupType reflect.Type
373var supportedDpis = []string{"ldpi", "mdpi", "hdpi", "xhdpi", "xxhdpi", "xxxhdpi"}
374
375func initAndroidAppImportVariantGroupTypes() {
376 dpiVariantGroupType = createVariantGroupType(supportedDpis, "Dpi_variants")
377
378 archNames := make([]string, len(android.ArchTypeList()))
379 for i, archType := range android.ArchTypeList() {
380 archNames[i] = archType.Name
381 }
382 archVariantGroupType = createVariantGroupType(archNames, "Arch")
383}
384
385// Populates all variant struct properties at creation time.
386func (a *AndroidAppImport) populateAllVariantStructs() {
387 a.dpiVariants = reflect.New(dpiVariantGroupType).Interface()
388 a.AddProperties(a.dpiVariants)
389
390 a.archVariants = reflect.New(archVariantGroupType).Interface()
391 a.AddProperties(a.archVariants)
392}
393
394func (a *AndroidAppImport) Privileged() bool {
395 return Bool(a.properties.Privileged)
396}
397
398func (a *AndroidAppImport) DepIsInSameApex(_ android.BaseModuleContext, _ android.Module) bool {
399 // android_app_import might have extra dependencies via uses_libs property.
400 // Don't track the dependency as we don't automatically add those libraries
401 // to the classpath. It should be explicitly added to java_libs property of APEX
402 return false
403}
404
Jiyong Park92315372021-04-02 08:45:46 +0900405func (a *AndroidAppImport) SdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
406 return android.SdkSpecPrivate
Jaewoong Jungf9b44652020-12-21 12:29:12 -0800407}
408
Jiyong Park92315372021-04-02 08:45:46 +0900409func (a *AndroidAppImport) MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
410 return android.SdkSpecPrivate
Jaewoong Jungf9b44652020-12-21 12:29:12 -0800411}
412
413var _ android.ApexModule = (*AndroidAppImport)(nil)
414
415// Implements android.ApexModule
416func (j *AndroidAppImport) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
417 sdkVersion android.ApiLevel) error {
418 // Do not check for prebuilts against the min_sdk_version of enclosing APEX
419 return nil
420}
421
422func createVariantGroupType(variants []string, variantGroupName string) reflect.Type {
423 props := reflect.TypeOf((*AndroidAppImportProperties)(nil))
424
425 variantFields := make([]reflect.StructField, len(variants))
426 for i, variant := range variants {
427 variantFields[i] = reflect.StructField{
428 Name: proptools.FieldNameForProperty(variant),
429 Type: props,
430 }
431 }
432
433 variantGroupStruct := reflect.StructOf(variantFields)
434 return reflect.StructOf([]reflect.StructField{
435 {
436 Name: variantGroupName,
437 Type: variantGroupStruct,
438 },
439 })
440}
441
442// android_app_import imports a prebuilt apk with additional processing specified in the module.
443// DPI-specific apk source files can be specified using dpi_variants. Example:
444//
445// android_app_import {
446// name: "example_import",
447// apk: "prebuilts/example.apk",
448// dpi_variants: {
449// mdpi: {
450// apk: "prebuilts/example_mdpi.apk",
451// },
452// xhdpi: {
453// apk: "prebuilts/example_xhdpi.apk",
454// },
455// },
456// certificate: "PRESIGNED",
457// }
458func AndroidAppImportFactory() android.Module {
459 module := &AndroidAppImport{}
460 module.AddProperties(&module.properties)
461 module.AddProperties(&module.dexpreoptProperties)
462 module.AddProperties(&module.usesLibrary.usesLibraryProperties)
463 module.populateAllVariantStructs()
464 android.AddLoadHook(module, func(ctx android.LoadHookContext) {
465 module.processVariants(ctx)
466 })
467
468 android.InitApexModule(module)
469 android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
470 android.InitDefaultableModule(module)
471 android.InitSingleSourcePrebuiltModule(module, &module.properties, "Apk")
472
Ulya Trafimovich22890c42021-01-05 12:04:17 +0000473 module.usesLibrary.enforce = true
474
Jaewoong Jungf9b44652020-12-21 12:29:12 -0800475 return module
476}
477
478type androidTestImportProperties struct {
479 // Whether the prebuilt apk can be installed without additional processing. Default is false.
480 Preprocessed *bool
481}
482
483type AndroidTestImport struct {
484 AndroidAppImport
485
486 testProperties testProperties
487
488 testImportProperties androidTestImportProperties
489
490 data android.Paths
491}
492
493func (a *AndroidTestImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
494 a.preprocessed = Bool(a.testImportProperties.Preprocessed)
495
496 a.generateAndroidBuildActions(ctx)
497
498 a.data = android.PathsForModuleSrc(ctx, a.testProperties.Data)
499}
500
501func (a *AndroidTestImport) InstallInTestcases() bool {
502 return true
503}
504
505// android_test_import imports a prebuilt test apk with additional processing specified in the
506// module. DPI or arch variant configurations can be made as with android_app_import.
507func AndroidTestImportFactory() android.Module {
508 module := &AndroidTestImport{}
509 module.AddProperties(&module.properties)
510 module.AddProperties(&module.dexpreoptProperties)
511 module.AddProperties(&module.testProperties)
512 module.AddProperties(&module.testImportProperties)
513 module.populateAllVariantStructs()
514 android.AddLoadHook(module, func(ctx android.LoadHookContext) {
515 module.processVariants(ctx)
516 })
517
518 module.dexpreopter.isTest = true
519
520 android.InitApexModule(module)
521 android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
522 android.InitDefaultableModule(module)
523 android.InitSingleSourcePrebuiltModule(module, &module.properties, "Apk")
524
525 return module
526}