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