Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e7d52c77 authored by Jaewoong Jung's avatar Jaewoong Jung Committed by Gerrit Code Review
Browse files

Merge "Add android_app_import."

parents b5ad835d ccbb3936
Loading
Loading
Loading
Loading
+26 −0
Original line number Diff line number Diff line
@@ -590,6 +590,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 {
+158 −24
Original line number Diff line number Diff line
@@ -34,6 +34,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
@@ -302,37 +303,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 != "" {
// 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)
		a.certificate = Certificate{
			defaultDir.Join(ctx, cert+".x509.pem"),
			defaultDir.Join(ctx, cert+".pk8"),
			mainCert = Certificate{
				defaultDir.Join(ctx, certPropValue+".x509.pem"),
				defaultDir.Join(ctx, certPropValue+".pk8"),
			}
		} else {
			pem, key := ctx.Config().DefaultAppCertificate(ctx)
		a.certificate = Certificate{pem, key}
			mainCert = 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) generateAndroidBuildActions(ctx android.ModuleContext) {
@@ -346,25 +348,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)
	}

@@ -390,7 +393,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

@@ -412,7 +415,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 {
@@ -620,3 +622,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
}
+7 −2
Original line number Diff line number Diff line
@@ -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{
+84 −0
Original line number Diff line number Diff line
@@ -968,3 +968,87 @@ func TestOverrideAndroidApp(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")
	}
}
+8 −5
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ type dexpreopter struct {
	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,
Loading