aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJaewoong Jung <jungjw@google.com>2019-04-15 09:48:31 -0700
committerRashed Abdel-Tawab <rashed@linux.com>2019-09-27 20:30:59 -0700
commit10944252b2237cf5950d1fea443603f9603d394e (patch)
treeb8df08fab05f4b8e1bf4b51ddebec2f4f149c7c3
parentb073f3cd7bf3af2e4220a83d2627234bd08315cf (diff)
downloadbuild_soong-10944252b2237cf5950d1fea443603f9603d394e.tar.gz
build_soong-10944252b2237cf5950d1fea443603f9603d394e.tar.bz2
build_soong-10944252b2237cf5950d1fea443603f9603d394e.zip
Add android_app_import.
This is an initial version that handles the most basic cases. Bug: 128610294 Test: app_test.go + prebuilt webview.apk Change-Id: Ic525559aad5612987e50aa75b326b77b23acb716
-rw-r--r--java/androidmk.go26
-rw-r--r--java/app.go182
-rw-r--r--java/app_builder.go9
-rw-r--r--java/app_test.go84
-rw-r--r--java/dexpreopt.go13
-rw-r--r--java/java_test.go3
6 files changed, 286 insertions, 31 deletions
diff --git a/java/androidmk.go b/java/androidmk.go
index b04f8050..78815838 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -581,6 +581,32 @@ func (dstubs *Droidstubs) AndroidMk() android.AndroidMkData {
}
}
+func (app *AndroidAppImport) AndroidMk() android.AndroidMkData {
+ return android.AndroidMkData{
+ Class: "APPS",
+ OutputFile: android.OptionalPathForPath(app.outputFile),
+ Include: "$(BUILD_SYSTEM)/soong_app_prebuilt.mk",
+ Extra: []android.AndroidMkExtraFunc{
+ func(w io.Writer, outputFile android.Path) {
+ if Bool(app.properties.Privileged) {
+ fmt.Fprintln(w, "LOCAL_PRIVILEGED_MODULE := true")
+ }
+ if app.certificate != nil {
+ fmt.Fprintln(w, "LOCAL_CERTIFICATE :=", app.certificate.Pem.String())
+ } else {
+ fmt.Fprintln(w, "LOCAL_CERTIFICATE := PRESIGNED")
+ }
+ if len(app.properties.Overrides) > 0 {
+ fmt.Fprintln(w, "LOCAL_OVERRIDES_PACKAGES :=", strings.Join(app.properties.Overrides, " "))
+ }
+ if len(app.dexpreopter.builtInstalled) > 0 {
+ fmt.Fprintln(w, "LOCAL_SOONG_BUILT_INSTALLED :=", app.dexpreopter.builtInstalled)
+ }
+ },
+ },
+ }
+}
+
func androidMkWriteTestData(data android.Paths, ret *android.AndroidMkData) {
var testFiles []string
for _, d := range data {
diff --git a/java/app.go b/java/app.go
index ece87802..cad90b03 100644
--- a/java/app.go
+++ b/java/app.go
@@ -35,6 +35,7 @@ func init() {
android.RegisterModuleType("android_test_helper_app", AndroidTestHelperAppFactory)
android.RegisterModuleType("android_app_certificate", AndroidAppCertificateFactory)
android.RegisterModuleType("override_android_app", OverrideAndroidAppModuleFactory)
+ android.RegisterModuleType("android_app_import", AndroidAppImportFactory)
}
// AndroidManifest.xml merging
@@ -308,37 +309,38 @@ func (a *AndroidApp) jniBuildActions(jniLibs []jniLib, ctx android.ModuleContext
return jniJarFile
}
-func (a *AndroidApp) certificateBuildActions(certificateDeps []Certificate, ctx android.ModuleContext) []Certificate {
- cert := a.getCertString(ctx)
- 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"),
+// Reads and prepends a main cert from the default cert dir if it hasn't been set already, i.e. it
+// isn't a cert module reference. Also checks and enforces system cert restriction if applicable.
+func processMainCert(m android.ModuleBase, certPropValue string, certificates []Certificate, ctx android.ModuleContext) []Certificate {
+ if android.SrcIsModule(certPropValue) == "" {
+ var mainCert Certificate
+ if certPropValue != "" {
+ defaultDir := ctx.Config().DefaultAppCertificateDir(ctx)
+ mainCert = Certificate{
+ defaultDir.Join(ctx, certPropValue+".x509.pem"),
+ defaultDir.Join(ctx, certPropValue+".pk8"),
+ }
+ } else {
+ pem, key := ctx.Config().DefaultAppCertificate(ctx)
+ mainCert = Certificate{pem, key}
}
- } else {
- pem, key := ctx.Config().DefaultAppCertificate(ctx)
- a.certificate = Certificate{pem, key}
+ certificates = append([]Certificate{mainCert}, certificates...)
}
- if !a.Module.Platform() {
- certPath := a.certificate.Pem.String()
+ if !m.Platform() {
+ certPath := certificates[0].Pem.String()
systemCertPath := ctx.Config().DefaultAppCertificateDir(ctx).String()
if strings.HasPrefix(certPath, systemCertPath) {
enforceSystemCert := ctx.Config().EnforceSystemCertificate()
whitelist := ctx.Config().EnforceSystemCertificateWhitelist()
- if enforceSystemCert && !inList(a.Module.Name(), whitelist) {
+ if enforceSystemCert && !inList(m.Name(), whitelist) {
ctx.PropertyErrorf("certificate", "The module in product partition cannot be signed with certificate in system.")
}
}
}
- return append([]Certificate{a.certificate}, certificateDeps...)
+ return certificates
}
func (a *AndroidApp) noticeBuildActions(ctx android.ModuleContext, installDir android.OutputPath) android.OptionalPath {
@@ -419,25 +421,26 @@ func (a *AndroidApp) generateAndroidBuildActions(ctx android.ModuleContext) {
dexJarFile := a.dexBuildActions(ctx)
- jniLibs, certificateDeps := a.collectAppDeps(ctx)
+ jniLibs, certificateDeps := collectAppDeps(ctx)
jniJarFile := a.jniBuildActions(jniLibs, ctx)
if ctx.Failed() {
return
}
- certificates := a.certificateBuildActions(certificateDeps, ctx)
+ certificates := processMainCert(a.ModuleBase, a.getCertString(ctx), certificateDeps, ctx)
+ a.certificate = certificates[0]
// Build a final signed app package.
// TODO(jungjw): Consider changing this to installApkName.
packageFile := android.PathForModuleOut(ctx, ctx.ModuleName()+".apk")
- CreateAppPackage(ctx, packageFile, a.exportPackage, jniJarFile, dexJarFile, certificates)
+ CreateAndSignAppPackage(ctx, packageFile, a.exportPackage, jniJarFile, dexJarFile, certificates)
a.outputFile = packageFile
for _, split := range a.aapt.splits {
// Sign the split APKs
packageFile := android.PathForModuleOut(ctx, ctx.ModuleName()+"_"+split.suffix+".apk")
- CreateAppPackage(ctx, packageFile, split.path, nil, nil, certificates)
+ CreateAndSignAppPackage(ctx, packageFile, split.path, nil, nil, certificates)
a.extraOutputFiles = append(a.extraOutputFiles, packageFile)
}
@@ -453,7 +456,7 @@ func (a *AndroidApp) generateAndroidBuildActions(ctx android.ModuleContext) {
}
}
-func (a *AndroidApp) collectAppDeps(ctx android.ModuleContext) ([]jniLib, []Certificate) {
+func collectAppDeps(ctx android.ModuleContext) ([]jniLib, []Certificate) {
var jniLibs []jniLib
var certificates []Certificate
@@ -475,7 +478,6 @@ func (a *AndroidApp) collectAppDeps(ctx android.ModuleContext) ([]jniLib, []Cert
}
} else {
ctx.ModuleErrorf("jni_libs dependency %q must be a cc library", otherName)
-
}
} else if tag == certificateTag {
if dep, ok := module.(*AndroidAppCertificate); ok {
@@ -683,3 +685,135 @@ func OverrideAndroidAppModuleFactory() android.Module {
android.InitOverrideModule(m)
return m
}
+
+type AndroidAppImport struct {
+ android.ModuleBase
+ android.DefaultableModuleBase
+ prebuilt android.Prebuilt
+
+ properties AndroidAppImportProperties
+
+ outputFile android.Path
+ certificate *Certificate
+
+ dexpreopter
+}
+
+type AndroidAppImportProperties struct {
+ // A prebuilt apk to import
+ Apk string
+
+ // 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
+
+ // Set this flag to true if the prebuilt apk is already signed. The certificate property must not
+ // be set for presigned modules.
+ Presigned *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
+
+ // 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
+}
+
+func (a *AndroidAppImport) DepsMutator(ctx android.BottomUpMutatorContext) {
+ cert := android.SrcIsModule(String(a.properties.Certificate))
+ if cert != "" {
+ ctx.AddDependency(ctx.Module(), certificateTag, cert)
+ }
+}
+
+func (a *AndroidAppImport) uncompressEmbeddedJniLibs(
+ ctx android.ModuleContext, inputPath android.Path, outputPath android.OutputPath) {
+ rule := android.NewRuleBuilder()
+ rule.Command().
+ Textf(`if (zipinfo %s 'lib/*.so' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then`, inputPath).
+ Tool(ctx.Config().HostToolPath(ctx, "zip2zip")).
+ FlagWithInput("-i ", inputPath).
+ FlagWithOutput("-o ", outputPath).
+ FlagWithArg("-0 ", "'lib/**/*.so'").
+ Textf(`; else cp -f %s %s; fi`, inputPath, outputPath)
+ rule.Build(pctx, ctx, "uncompress-embedded-jni-libs", "Uncompress embedded JIN libs")
+}
+
+func (a *AndroidAppImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ if String(a.properties.Certificate) == "" && !Bool(a.properties.Presigned) {
+ ctx.PropertyErrorf("certificate", "No certificate specified for prebuilt")
+ }
+ if String(a.properties.Certificate) != "" && Bool(a.properties.Presigned) {
+ ctx.PropertyErrorf("certificate", "Certificate can't be specified for presigned modules")
+ }
+
+ _, certificates := collectAppDeps(ctx)
+
+ // TODO: LOCAL_EXTRACT_APK/LOCAL_EXTRACT_DPI_APK
+ // TODO: LOCAL_DPI_VARIANTS
+ // TODO: LOCAL_PACKAGE_SPLITS
+
+ srcApk := a.prebuilt.SingleSourcePath(ctx)
+
+ // TODO: Install or embed JNI libraries
+
+ // Uncompress JNI libraries in the apk
+ jnisUncompressed := android.PathForModuleOut(ctx, "jnis-uncompressed", ctx.ModuleName()+".apk")
+ a.uncompressEmbeddedJniLibs(ctx, srcApk, jnisUncompressed.OutputPath)
+
+ // TODO: Uncompress dex if applicable
+
+ installDir := android.PathForModuleInstall(ctx, "app", a.BaseModuleName())
+ a.dexpreopter.installPath = installDir.Join(ctx, a.BaseModuleName()+".apk")
+ a.dexpreopter.isInstallable = true
+ a.dexpreopter.isPresignedPrebuilt = Bool(a.properties.Presigned)
+ dexOutput := a.dexpreopter.dexpreopt(ctx, jnisUncompressed)
+
+ // Sign or align the package
+ // TODO: Handle EXTERNAL
+ if !Bool(a.properties.Presigned) {
+ certificates = processMainCert(a.ModuleBase, *a.properties.Certificate, certificates, ctx)
+ if len(certificates) != 1 {
+ ctx.ModuleErrorf("Unexpected number of certificates were extracted: %q", certificates)
+ }
+ a.certificate = &certificates[0]
+ signed := android.PathForModuleOut(ctx, "signed", ctx.ModuleName()+".apk")
+ SignAppPackage(ctx, signed, dexOutput, certificates)
+ a.outputFile = signed
+ } else {
+ alignedApk := android.PathForModuleOut(ctx, "zip-aligned", ctx.ModuleName()+".apk")
+ TransformZipAlign(ctx, alignedApk, dexOutput)
+ a.outputFile = alignedApk
+ }
+
+ // TODO: Optionally compress the output apk.
+
+ ctx.InstallFile(installDir, a.BaseModuleName()+".apk", a.outputFile)
+
+ // TODO: androidmk converter jni libs
+}
+
+func (a *AndroidAppImport) Prebuilt() *android.Prebuilt {
+ return &a.prebuilt
+}
+
+func (a *AndroidAppImport) Name() string {
+ return a.prebuilt.Name(a.ModuleBase.Name())
+}
+
+// android_app_import imports a prebuilt apk with additional processing specified in the module.
+func AndroidAppImportFactory() android.Module {
+ module := &AndroidAppImport{}
+ module.AddProperties(&module.properties)
+ module.AddProperties(&module.dexpreoptProperties)
+
+ InitJavaModule(module, android.DeviceSupported)
+ android.InitSingleSourcePrebuiltModule(module, &module.properties.Apk)
+
+ return module
+}
diff --git a/java/app_builder.go b/java/app_builder.go
index 5bacb677..82a390f6 100644
--- a/java/app_builder.go
+++ b/java/app_builder.go
@@ -62,7 +62,7 @@ var combineApk = pctx.AndroidStaticRule("combineApk",
CommandDeps: []string{"${config.MergeZipsCmd}"},
})
-func CreateAppPackage(ctx android.ModuleContext, outputFile android.WritablePath,
+func CreateAndSignAppPackage(ctx android.ModuleContext, outputFile android.WritablePath,
packageFile, jniJarFile, dexJarFile android.Path, certificates []Certificate) {
unsignedApkName := strings.TrimSuffix(outputFile.Base(), ".apk") + "-unsigned.apk"
@@ -83,6 +83,11 @@ func CreateAppPackage(ctx android.ModuleContext, outputFile android.WritablePath
Output: unsignedApk,
})
+ SignAppPackage(ctx, outputFile, unsignedApk, certificates)
+}
+
+func SignAppPackage(ctx android.ModuleContext, signedApk android.WritablePath, unsignedApk android.Path, certificates []Certificate) {
+
var certificateArgs []string
var deps android.Paths
for _, c := range certificates {
@@ -93,7 +98,7 @@ func CreateAppPackage(ctx android.ModuleContext, outputFile android.WritablePath
ctx.Build(pctx, android.BuildParams{
Rule: Signapk,
Description: "signapk",
- Output: outputFile,
+ Output: signedApk,
Input: unsignedApk,
Implicits: deps,
Args: map[string]string{
diff --git a/java/app_test.go b/java/app_test.go
index 5c19d9a6..66f993dc 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -1151,3 +1151,87 @@ func TestUncompressDex(t *testing.T) {
})
}
}
+
+func TestAndroidAppImport(t *testing.T) {
+ ctx := testJava(t, `
+ android_app_import {
+ name: "foo",
+ apk: "prebuilts/apk/app.apk",
+ certificate: "platform",
+ dex_preopt: {
+ enabled: true,
+ },
+ }
+ `)
+
+ variant := ctx.ModuleForTests("foo", "android_common")
+
+ // Check dexpreopt outputs.
+ if variant.MaybeOutput("dexpreopt/oat/arm64/package.vdex").Rule == nil ||
+ variant.MaybeOutput("dexpreopt/oat/arm64/package.odex").Rule == nil {
+ t.Errorf("can't find dexpreopt outputs")
+ }
+
+ // Check cert signing flag.
+ signedApk := variant.Output("signed/foo.apk")
+ signingFlag := signedApk.Args["certificates"]
+ expected := "build/make/target/product/security/platform.x509.pem build/make/target/product/security/platform.pk8"
+ if expected != signingFlag {
+ t.Errorf("Incorrect signing flags, expected: %q, got: %q", expected, signingFlag)
+ }
+}
+
+func TestAndroidAppImport_NoDexPreopt(t *testing.T) {
+ ctx := testJava(t, `
+ android_app_import {
+ name: "foo",
+ apk: "prebuilts/apk/app.apk",
+ certificate: "platform",
+ dex_preopt: {
+ enabled: false,
+ },
+ }
+ `)
+
+ variant := ctx.ModuleForTests("foo", "android_common")
+
+ // Check dexpreopt outputs. They shouldn't exist.
+ if variant.MaybeOutput("dexpreopt/oat/arm64/package.vdex").Rule != nil ||
+ variant.MaybeOutput("dexpreopt/oat/arm64/package.odex").Rule != nil {
+ t.Errorf("dexpreopt shouldn't have run.")
+ }
+}
+
+func TestAndroidAppImport_Presigned(t *testing.T) {
+ ctx := testJava(t, `
+ android_app_import {
+ name: "foo",
+ apk: "prebuilts/apk/app.apk",
+ presigned: true,
+ dex_preopt: {
+ enabled: true,
+ },
+ }
+ `)
+
+ variant := ctx.ModuleForTests("foo", "android_common")
+
+ // Check dexpreopt outputs.
+ if variant.MaybeOutput("dexpreopt/oat/arm64/package.vdex").Rule == nil ||
+ variant.MaybeOutput("dexpreopt/oat/arm64/package.odex").Rule == nil {
+ t.Errorf("can't find dexpreopt outputs")
+ }
+ // Make sure stripping wasn't done.
+ stripRule := variant.Output("dexpreopt/foo.apk")
+ if !strings.HasPrefix(stripRule.RuleParams.Command, "cp -f") {
+ t.Errorf("unexpected, non-skipping strip command: %q", stripRule.RuleParams.Command)
+ }
+
+ // Make sure signing was skipped and aligning was done instead.
+ if variant.MaybeOutput("signed/foo.apk").Rule != nil {
+ t.Errorf("signing rule shouldn't be included.")
+ }
+ if variant.MaybeOutput("zip-aligned/foo.apk").Rule == nil {
+ t.Errorf("can't find aligning rule")
+ }
+}
diff --git a/java/dexpreopt.go b/java/dexpreopt.go
index 9141f9ef..088c12df 100644
--- a/java/dexpreopt.go
+++ b/java/dexpreopt.go
@@ -22,11 +22,12 @@ import (
type dexpreopter struct {
dexpreoptProperties DexpreoptProperties
- installPath android.OutputPath
- uncompressedDex bool
- isSDKLibrary bool
- isTest bool
- isInstallable bool
+ installPath android.OutputPath
+ uncompressedDex bool
+ isSDKLibrary bool
+ isTest bool
+ isInstallable bool
+ isPresignedPrebuilt bool
builtInstalled string
}
@@ -177,6 +178,8 @@ func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.Mo
NoCreateAppImage: !BoolDefault(d.dexpreoptProperties.Dex_preopt.App_image, true),
ForceCreateAppImage: BoolDefault(d.dexpreoptProperties.Dex_preopt.App_image, false),
+ PresignedPrebuilt: d.isPresignedPrebuilt,
+
NoStripping: Bool(d.dexpreoptProperties.Dex_preopt.No_stripping),
StripInputPath: dexJarFile,
StripOutputPath: strippedDexJarFile.OutputPath,
diff --git a/java/java_test.go b/java/java_test.go
index 90849c2f..60ea37ce 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -63,6 +63,7 @@ func testContext(config android.Config, bp string,
ctx := android.NewTestArchContext()
ctx.RegisterModuleType("android_app", android.ModuleFactoryAdaptor(AndroidAppFactory))
ctx.RegisterModuleType("android_app_certificate", android.ModuleFactoryAdaptor(AndroidAppCertificateFactory))
+ ctx.RegisterModuleType("android_app_import", android.ModuleFactoryAdaptor(AndroidAppImportFactory))
ctx.RegisterModuleType("android_library", android.ModuleFactoryAdaptor(AndroidLibraryFactory))
ctx.RegisterModuleType("android_test", android.ModuleFactoryAdaptor(AndroidTestFactory))
ctx.RegisterModuleType("android_test_helper_app", android.ModuleFactoryAdaptor(AndroidTestHelperAppFactory))
@@ -167,6 +168,8 @@ func testContext(config android.Config, bp string,
"prebuilts/sdk/tools/core-lambda-stubs.jar": nil,
"prebuilts/sdk/Android.bp": []byte(`prebuilt_apis { name: "sdk", api_dirs: ["14", "28", "current"],}`),
+ "prebuilts/apk/app.apk": nil,
+
// For framework-res, which is an implicit dependency for framework
"AndroidManifest.xml": nil,
"build/make/target/product/security/testkey": nil,