Privileged APKs need to store their dex files uncompressed so they can be verified and mapped directly out of the APK. Also track whether the module will be dexpreopted or not in order to determine if the dex file should be stripped before signing. Test: SystemUI.apk contains an uncompressed dex file Change-Id: I4dca86c7f8778595882405b34adcf2a7bae03c67
470 lines
15 KiB
Go
470 lines
15 KiB
Go
// Copyright 2015 Google Inc. All rights reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package java
|
|
|
|
// This file contains the module types for compiling Android apps.
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"github.com/google/blueprint"
|
|
"github.com/google/blueprint/proptools"
|
|
|
|
"android/soong/android"
|
|
"android/soong/cc"
|
|
"android/soong/tradefed"
|
|
)
|
|
|
|
func init() {
|
|
android.RegisterModuleType("android_app", AndroidAppFactory)
|
|
android.RegisterModuleType("android_test", AndroidTestFactory)
|
|
android.RegisterModuleType("android_test_helper_app", AndroidTestHelperAppFactory)
|
|
android.RegisterModuleType("android_app_certificate", AndroidAppCertificateFactory)
|
|
}
|
|
|
|
// AndroidManifest.xml merging
|
|
// package splits
|
|
|
|
type appProperties struct {
|
|
// The name of a certificate in the default certificate directory, blank to use the default product certificate,
|
|
// or an android_app_certificate module name in the form ":module".
|
|
Certificate *string
|
|
|
|
// Names of extra android_app_certificate modules to sign the apk with in the form ":module".
|
|
Additional_certificates []string
|
|
|
|
// If set, create package-export.apk, which other packages can
|
|
// use to get PRODUCT-agnostic resource data like IDs and type definitions.
|
|
Export_package_resources *bool
|
|
|
|
// Specifies that this app should be installed to the priv-app directory,
|
|
// where the system will grant it additional privileges not available to
|
|
// normal apps.
|
|
Privileged *bool
|
|
|
|
// list of resource labels to generate individual resource packages
|
|
Package_splits []string
|
|
|
|
// Names of modules to be overridden. Listed modules can only be other binaries
|
|
// (in Make or Soong).
|
|
// This does not completely prevent installation of the overridden binaries, but if both
|
|
// binaries would be installed by default (in PRODUCT_PACKAGES) the other binary will be removed
|
|
// from PRODUCT_PACKAGES.
|
|
Overrides []string
|
|
|
|
// list of native libraries that will be provided in or alongside the resulting jar
|
|
Jni_libs []string `android:"arch_variant"`
|
|
|
|
AllowDexPreopt bool `blueprint:"mutated"`
|
|
EmbedJNI bool `blueprint:"mutated"`
|
|
StripDex bool `blueprint:"mutated"`
|
|
}
|
|
|
|
type AndroidApp struct {
|
|
Library
|
|
aapt
|
|
|
|
certificate certificate
|
|
|
|
appProperties appProperties
|
|
|
|
extraLinkFlags []string
|
|
|
|
installJniLibs []jniLib
|
|
}
|
|
|
|
func (a *AndroidApp) ExportedProguardFlagFiles() android.Paths {
|
|
return nil
|
|
}
|
|
|
|
func (a *AndroidApp) ExportedStaticPackages() android.Paths {
|
|
return nil
|
|
}
|
|
|
|
func (a *AndroidApp) ExportedManifest() android.Path {
|
|
return a.manifestPath
|
|
}
|
|
|
|
var _ AndroidLibraryDependency = (*AndroidApp)(nil)
|
|
|
|
type certificate struct {
|
|
pem, key android.Path
|
|
}
|
|
|
|
func (a *AndroidApp) DepsMutator(ctx android.BottomUpMutatorContext) {
|
|
a.Module.deps(ctx)
|
|
|
|
if !Bool(a.properties.No_framework_libs) && !Bool(a.properties.No_standard_libs) {
|
|
a.aapt.deps(ctx, sdkContext(a))
|
|
}
|
|
|
|
for _, jniTarget := range ctx.MultiTargets() {
|
|
variation := []blueprint.Variation{
|
|
{Mutator: "arch", Variation: jniTarget.String()},
|
|
{Mutator: "link", Variation: "shared"},
|
|
}
|
|
tag := &jniDependencyTag{
|
|
target: jniTarget,
|
|
}
|
|
ctx.AddFarVariationDependencies(variation, tag, a.appProperties.Jni_libs...)
|
|
}
|
|
|
|
cert := android.SrcIsModule(String(a.appProperties.Certificate))
|
|
if cert != "" {
|
|
ctx.AddDependency(ctx.Module(), certificateTag, cert)
|
|
}
|
|
|
|
for _, cert := range a.appProperties.Additional_certificates {
|
|
cert = android.SrcIsModule(cert)
|
|
if cert != "" {
|
|
ctx.AddDependency(ctx.Module(), certificateTag, cert)
|
|
} else {
|
|
ctx.PropertyErrorf("additional_certificates",
|
|
`must be names of android_app_certificate modules in the form ":module"`)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (a *AndroidApp) GenerateAndroidBuildActions(ctx android.ModuleContext) {
|
|
a.generateAndroidBuildActions(ctx)
|
|
}
|
|
|
|
// Returns whether this module should have the dex file stored uncompressed in the APK, or stripped completely. If
|
|
// stripped, the code will still be present on the device in the dexpreopted files.
|
|
// This is only necessary for APKs, and not jars, because APKs are signed and the dex file should not be uncompressed
|
|
// or removed after the signature has been generated. For jars, which are not signed, the dex file is uncompressed
|
|
// or removed at installation time in Make.
|
|
func (a *AndroidApp) uncompressOrStripDex(ctx android.ModuleContext) (uncompress, strip bool) {
|
|
if ctx.Config().UnbundledBuild() {
|
|
return false, false
|
|
}
|
|
|
|
strip = ctx.Config().DefaultStripDex()
|
|
// TODO(ccross): don't strip dex installed on partitions that may be updated separately (like vendor)
|
|
// TODO(ccross): don't strip dex on modules with LOCAL_APK_LIBRARIES equivalent
|
|
// TODO(ccross): don't strip dex on modules that are preopted to system_other
|
|
|
|
// Uncompress dex in APKs of privileged apps, and modules used by privileged apps.
|
|
if ctx.Config().UncompressPrivAppDex() &&
|
|
(Bool(a.appProperties.Privileged) ||
|
|
inList(ctx.ModuleName(), ctx.Config().ModulesLoadedByPrivilegedModules())) {
|
|
|
|
uncompress = true
|
|
// If the dex files is store uncompressed, don't strip it, we will reuse the uncompressed dex from the APK
|
|
// instead of copying it into the odex file.
|
|
strip = false
|
|
}
|
|
|
|
// If dexpreopt is disabled, don't strip the dex file
|
|
if !a.appProperties.AllowDexPreopt ||
|
|
!BoolDefault(a.deviceProperties.Dex_preopt.Enabled, true) ||
|
|
ctx.Config().DisableDexPreopt(ctx.ModuleName()) {
|
|
strip = false
|
|
}
|
|
|
|
return uncompress, strip
|
|
}
|
|
|
|
func (a *AndroidApp) generateAndroidBuildActions(ctx android.ModuleContext) {
|
|
linkFlags := append([]string(nil), a.extraLinkFlags...)
|
|
|
|
hasProduct := false
|
|
for _, f := range a.aaptProperties.Aaptflags {
|
|
if strings.HasPrefix(f, "--product") {
|
|
hasProduct = true
|
|
}
|
|
}
|
|
|
|
// Product characteristics
|
|
if !hasProduct && len(ctx.Config().ProductAAPTCharacteristics()) > 0 {
|
|
linkFlags = append(linkFlags, "--product", ctx.Config().ProductAAPTCharacteristics())
|
|
}
|
|
|
|
// Product AAPT config
|
|
for _, aaptConfig := range ctx.Config().ProductAAPTConfig() {
|
|
linkFlags = append(linkFlags, "-c", aaptConfig)
|
|
}
|
|
|
|
// Product AAPT preferred config
|
|
if len(ctx.Config().ProductAAPTPreferredConfig()) > 0 {
|
|
linkFlags = append(linkFlags, "--preferred-density", ctx.Config().ProductAAPTPreferredConfig())
|
|
}
|
|
|
|
// TODO: LOCAL_PACKAGE_OVERRIDES
|
|
// $(addprefix --rename-manifest-package , $(PRIVATE_MANIFEST_PACKAGE_NAME)) \
|
|
|
|
a.aapt.buildActions(ctx, sdkContext(a), linkFlags...)
|
|
|
|
// apps manifests are handled by aapt, don't let Module see them
|
|
a.properties.Manifest = nil
|
|
|
|
var staticLibProguardFlagFiles android.Paths
|
|
ctx.VisitDirectDeps(func(m android.Module) {
|
|
if lib, ok := m.(AndroidLibraryDependency); ok && ctx.OtherModuleDependencyTag(m) == staticLibTag {
|
|
staticLibProguardFlagFiles = append(staticLibProguardFlagFiles, lib.ExportedProguardFlagFiles()...)
|
|
}
|
|
})
|
|
|
|
staticLibProguardFlagFiles = android.FirstUniquePaths(staticLibProguardFlagFiles)
|
|
|
|
a.Module.extraProguardFlagFiles = append(a.Module.extraProguardFlagFiles, staticLibProguardFlagFiles...)
|
|
a.Module.extraProguardFlagFiles = append(a.Module.extraProguardFlagFiles, a.proguardOptionsFile)
|
|
|
|
a.deviceProperties.UncompressDex, a.appProperties.StripDex = a.uncompressOrStripDex(ctx)
|
|
|
|
if ctx.ModuleName() != "framework-res" {
|
|
a.Module.compile(ctx, a.aaptSrcJar)
|
|
}
|
|
dexJarFile := a.dexJarFile
|
|
|
|
if a.appProperties.StripDex {
|
|
dexJarFile = nil
|
|
}
|
|
|
|
var certificates []certificate
|
|
|
|
var jniJarFile android.WritablePath
|
|
jniLibs, certificateDeps := a.collectAppDeps(ctx)
|
|
if len(jniLibs) > 0 {
|
|
embedJni := ctx.Config().UnbundledBuild() || a.appProperties.EmbedJNI
|
|
if embedJni {
|
|
jniJarFile = android.PathForModuleOut(ctx, "jnilibs.zip")
|
|
TransformJniLibsToJar(ctx, jniJarFile, jniLibs)
|
|
} else {
|
|
a.installJniLibs = jniLibs
|
|
}
|
|
}
|
|
|
|
if ctx.Failed() {
|
|
return
|
|
}
|
|
|
|
cert := String(a.appProperties.Certificate)
|
|
certModule := android.SrcIsModule(cert)
|
|
if certModule != "" {
|
|
a.certificate = certificateDeps[0]
|
|
certificateDeps = certificateDeps[1:]
|
|
} else if cert != "" {
|
|
defaultDir := ctx.Config().DefaultAppCertificateDir(ctx)
|
|
a.certificate = certificate{
|
|
defaultDir.Join(ctx, cert+".x509.pem"),
|
|
defaultDir.Join(ctx, cert+".pk8"),
|
|
}
|
|
} else {
|
|
pem, key := ctx.Config().DefaultAppCertificate(ctx)
|
|
a.certificate = certificate{pem, key}
|
|
}
|
|
|
|
certificates = append([]certificate{a.certificate}, certificateDeps...)
|
|
|
|
packageFile := android.PathForModuleOut(ctx, "package.apk")
|
|
CreateAppPackage(ctx, packageFile, a.exportPackage, jniJarFile, dexJarFile, certificates)
|
|
a.outputFile = packageFile
|
|
|
|
if ctx.ModuleName() == "framework-res" {
|
|
// framework-res.apk is installed as system/framework/framework-res.apk
|
|
ctx.InstallFile(android.PathForModuleInstall(ctx, "framework"), ctx.ModuleName()+".apk", a.outputFile)
|
|
} else if Bool(a.appProperties.Privileged) {
|
|
ctx.InstallFile(android.PathForModuleInstall(ctx, "priv-app"), ctx.ModuleName()+".apk", a.outputFile)
|
|
} else {
|
|
ctx.InstallFile(android.PathForModuleInstall(ctx, "app"), ctx.ModuleName()+".apk", a.outputFile)
|
|
}
|
|
}
|
|
|
|
func (a *AndroidApp) collectAppDeps(ctx android.ModuleContext) ([]jniLib, []certificate) {
|
|
var jniLibs []jniLib
|
|
var certificates []certificate
|
|
|
|
ctx.VisitDirectDeps(func(module android.Module) {
|
|
otherName := ctx.OtherModuleName(module)
|
|
tag := ctx.OtherModuleDependencyTag(module)
|
|
|
|
if jniTag, ok := tag.(*jniDependencyTag); ok {
|
|
if dep, ok := module.(*cc.Module); ok {
|
|
lib := dep.OutputFile()
|
|
if lib.Valid() {
|
|
jniLibs = append(jniLibs, jniLib{
|
|
name: ctx.OtherModuleName(module),
|
|
path: lib.Path(),
|
|
target: jniTag.target,
|
|
})
|
|
} else {
|
|
ctx.ModuleErrorf("dependency %q missing output file", otherName)
|
|
}
|
|
} else {
|
|
ctx.ModuleErrorf("jni_libs dependency %q must be a cc library", otherName)
|
|
|
|
}
|
|
} else if tag == certificateTag {
|
|
if dep, ok := module.(*AndroidAppCertificate); ok {
|
|
certificates = append(certificates, dep.certificate)
|
|
} else {
|
|
ctx.ModuleErrorf("certificate dependency %q must be an android_app_certificate module", otherName)
|
|
}
|
|
}
|
|
})
|
|
|
|
return jniLibs, certificates
|
|
}
|
|
|
|
func AndroidAppFactory() android.Module {
|
|
module := &AndroidApp{}
|
|
|
|
module.Module.deviceProperties.Optimize.Enabled = proptools.BoolPtr(true)
|
|
module.Module.deviceProperties.Optimize.Shrink = proptools.BoolPtr(true)
|
|
|
|
module.Module.properties.Instrument = true
|
|
module.Module.properties.Installable = proptools.BoolPtr(true)
|
|
module.appProperties.AllowDexPreopt = true
|
|
|
|
module.AddProperties(
|
|
&module.Module.properties,
|
|
&module.Module.deviceProperties,
|
|
&module.Module.protoProperties,
|
|
&module.aaptProperties,
|
|
&module.appProperties)
|
|
|
|
module.Prefer32(func(ctx android.BaseModuleContext, base *android.ModuleBase, class android.OsClass) bool {
|
|
return class == android.Device && ctx.Config().DevicePrefer32BitApps()
|
|
})
|
|
|
|
android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
|
|
android.InitDefaultableModule(module)
|
|
|
|
return module
|
|
}
|
|
|
|
type appTestProperties struct {
|
|
Instrumentation_for *string
|
|
}
|
|
|
|
type AndroidTest struct {
|
|
AndroidApp
|
|
|
|
appTestProperties appTestProperties
|
|
|
|
testProperties testProperties
|
|
|
|
testConfig android.Path
|
|
data android.Paths
|
|
}
|
|
|
|
func (a *AndroidTest) GenerateAndroidBuildActions(ctx android.ModuleContext) {
|
|
a.generateAndroidBuildActions(ctx)
|
|
|
|
a.testConfig = tradefed.AutoGenInstrumentationTestConfig(ctx, a.testProperties.Test_config, a.testProperties.Test_config_template, a.manifestPath)
|
|
a.data = ctx.ExpandSources(a.testProperties.Data, nil)
|
|
}
|
|
|
|
func (a *AndroidTest) DepsMutator(ctx android.BottomUpMutatorContext) {
|
|
android.ExtractSourceDeps(ctx, a.testProperties.Test_config)
|
|
android.ExtractSourceDeps(ctx, a.testProperties.Test_config_template)
|
|
android.ExtractSourcesDeps(ctx, a.testProperties.Data)
|
|
a.AndroidApp.DepsMutator(ctx)
|
|
if a.appTestProperties.Instrumentation_for != nil {
|
|
// The android_app dependency listed in instrumentation_for needs to be added to the classpath for javac,
|
|
// but not added to the aapt2 link includes like a normal android_app or android_library dependency, so
|
|
// use instrumentationForTag instead of libTag.
|
|
ctx.AddVariationDependencies(nil, instrumentationForTag, String(a.appTestProperties.Instrumentation_for))
|
|
}
|
|
}
|
|
|
|
func AndroidTestFactory() android.Module {
|
|
module := &AndroidTest{}
|
|
|
|
module.Module.deviceProperties.Optimize.Enabled = proptools.BoolPtr(true)
|
|
|
|
module.Module.properties.Instrument = true
|
|
module.Module.properties.Installable = proptools.BoolPtr(true)
|
|
module.appProperties.EmbedJNI = true
|
|
module.appProperties.AllowDexPreopt = false
|
|
|
|
module.AddProperties(
|
|
&module.Module.properties,
|
|
&module.Module.deviceProperties,
|
|
&module.Module.protoProperties,
|
|
&module.aaptProperties,
|
|
&module.appProperties,
|
|
&module.appTestProperties,
|
|
&module.testProperties)
|
|
|
|
android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
|
|
android.InitDefaultableModule(module)
|
|
return module
|
|
}
|
|
|
|
type appTestHelperAppProperties struct {
|
|
// list of compatibility suites (for example "cts", "vts") that the module should be
|
|
// installed into.
|
|
Test_suites []string `android:"arch_variant"`
|
|
}
|
|
|
|
type AndroidTestHelperApp struct {
|
|
AndroidApp
|
|
|
|
appTestHelperAppProperties appTestHelperAppProperties
|
|
}
|
|
|
|
func AndroidTestHelperAppFactory() android.Module {
|
|
module := &AndroidTestHelperApp{}
|
|
|
|
module.Module.deviceProperties.Optimize.Enabled = proptools.BoolPtr(true)
|
|
|
|
module.Module.properties.Installable = proptools.BoolPtr(true)
|
|
module.appProperties.EmbedJNI = true
|
|
module.appProperties.AllowDexPreopt = false
|
|
|
|
module.AddProperties(
|
|
&module.Module.properties,
|
|
&module.Module.deviceProperties,
|
|
&module.Module.protoProperties,
|
|
&module.aaptProperties,
|
|
&module.appProperties,
|
|
&module.appTestHelperAppProperties)
|
|
|
|
android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
|
|
android.InitDefaultableModule(module)
|
|
return module
|
|
}
|
|
|
|
type AndroidAppCertificate struct {
|
|
android.ModuleBase
|
|
properties AndroidAppCertificateProperties
|
|
certificate certificate
|
|
}
|
|
|
|
type AndroidAppCertificateProperties struct {
|
|
// Name of the certificate files. Extensions .x509.pem and .pk8 will be added to the name.
|
|
Certificate *string
|
|
}
|
|
|
|
func AndroidAppCertificateFactory() android.Module {
|
|
module := &AndroidAppCertificate{}
|
|
module.AddProperties(&module.properties)
|
|
android.InitAndroidModule(module)
|
|
return module
|
|
}
|
|
|
|
func (c *AndroidAppCertificate) DepsMutator(ctx android.BottomUpMutatorContext) {
|
|
}
|
|
|
|
func (c *AndroidAppCertificate) GenerateAndroidBuildActions(ctx android.ModuleContext) {
|
|
cert := String(c.properties.Certificate)
|
|
c.certificate = certificate{
|
|
android.PathForModuleSrc(ctx, cert+".x509.pem"),
|
|
android.PathForModuleSrc(ctx, cert+".pk8"),
|
|
}
|
|
}
|