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