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

Commit 3835509f authored by Cole Faust's avatar Cole Faust Committed by Gerrit Code Review
Browse files

Merge "Error out if skip_preprocessed_apk_checks is set when it's not necessary" into main

parents ece322f6 9c5c09f0
Loading
Loading
Loading
Loading
+32 −81
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package java
import (
	"fmt"
	"reflect"
	"strings"

	"github.com/google/blueprint"

@@ -51,27 +52,11 @@ var (
		Description: "Uncompress dex files",
	})

	checkDexLibsAreUncompressedRule = pctx.AndroidStaticRule("check-dex-libs-are-uncompressed", blueprint.RuleParams{
		// grep -v ' stor ' will search for lines that don't have ' stor '. stor means the file is stored uncompressed
		Command: "if (zipinfo $in '*.dex' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then " +
			"echo $in: Contains compressed JNI libraries and/or dex files >&2;" +
			"exit 1; " +
			"else " +
			"touch $out; " +
			"fi",
		Description: "Check for compressed JNI libs or dex files",
	})

	checkJniLibsAreUncompressedRule = pctx.AndroidStaticRule("check-jni-libs-are-uncompressed", blueprint.RuleParams{
		// grep -v ' stor ' will search for lines that don't have ' stor '. stor means the file is stored uncompressed
		Command: "if (zipinfo $in 'lib/*.so' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then " +
			"echo $in: Contains compressed JNI libraries >&2;" +
			"exit 1; " +
			"else " +
			"touch $out; " +
			"fi",
		Description: "Check for compressed JNI libs or dex files",
	})
	checkPresignedApkRule = pctx.AndroidStaticRule("check-presigned-apk", blueprint.RuleParams{
		Command:     "build/soong/scripts/check_prebuilt_presigned_apk.py --aapt2 ${config.Aapt2Cmd} --zipalign ${config.ZipAlign} $extraArgs $in $out",
		CommandDeps: []string{"build/soong/scripts/check_prebuilt_presigned_apk.py", "${config.Aapt2Cmd}", "${config.ZipAlign}"},
		Description: "Check presigned apk",
	}, "extraArgs")
)

func RegisterAppImportBuildComponents(ctx android.RegistrationContext) {
@@ -352,20 +337,14 @@ func (a *AndroidAppImport) generateAndroidBuildActions(ctx android.ModuleContext
	// Sign or align the package if package has not been preprocessed

	if proptools.Bool(a.properties.Preprocessed) {
		var output android.WritablePath
		if !proptools.Bool(a.properties.Skip_preprocessed_apk_checks) {
			output = android.PathForModuleOut(ctx, "validated-prebuilt", apkFilename)
			a.validatePreprocessedApk(ctx, srcApk, output)
		} else {
			// If using the input APK unmodified, still make a copy of it so that the output filename has the
			// right basename.
			output = android.PathForModuleOut(ctx, apkFilename)
		validationStamp := a.validatePresignedApk(ctx, srcApk)
		output := android.PathForModuleOut(ctx, apkFilename)
		ctx.Build(pctx, android.BuildParams{
			Rule:       android.Cp,
			Input:      srcApk,
			Output:     output,
			Validation: validationStamp,
		})
		}
		a.outputFile = output
		a.certificate = PresignedCertificate
	} else if !Bool(a.properties.Presigned) {
@@ -384,13 +363,9 @@ func (a *AndroidAppImport) generateAndroidBuildActions(ctx android.ModuleContext
		SignAppPackage(ctx, signed, jnisUncompressed, certificates, nil, lineageFile, rotationMinSdkVersion)
		a.outputFile = signed
	} else {
		// Presigned without Preprocessed shouldn't really be a thing, currently we disallow
		// it for apps with targetSdk >= 30, because on those targetSdks you must be using signature
		// v2 or later, and signature v2 would be wrecked by uncompressing libs / zipaligning.
		// But ideally we would disallow it for all prebuilt apks, and remove the presigned property.
		targetSdkCheck := a.validateTargetSdkLessThan30(ctx, srcApk)
		validationStamp := a.validatePresignedApk(ctx, srcApk)
		alignedApk := android.PathForModuleOut(ctx, "zip-aligned", apkFilename)
		TransformZipAlign(ctx, alignedApk, jnisUncompressed, []android.Path{targetSdkCheck})
		TransformZipAlign(ctx, alignedApk, jnisUncompressed, []android.Path{validationStamp})
		a.outputFile = alignedApk
		a.certificate = PresignedCertificate
	}
@@ -406,52 +381,28 @@ func (a *AndroidAppImport) generateAndroidBuildActions(ctx android.ModuleContext
	// TODO: androidmk converter jni libs
}

func (a *AndroidAppImport) validatePreprocessedApk(ctx android.ModuleContext, srcApk android.Path, dstApk android.WritablePath) {
	var validations android.Paths

	alignmentStamp := android.PathForModuleOut(ctx, "validated-prebuilt", "alignment.stamp")
	ctx.Build(pctx, android.BuildParams{
		Rule:   checkZipAlignment,
		Input:  srcApk,
		Output: alignmentStamp,
	})

	validations = append(validations, alignmentStamp)
	jniCompressionStamp := android.PathForModuleOut(ctx, "validated-prebuilt", "jni_compression.stamp")
	ctx.Build(pctx, android.BuildParams{
		Rule:   checkJniLibsAreUncompressedRule,
		Input:  srcApk,
		Output: jniCompressionStamp,
	})
	validations = append(validations, jniCompressionStamp)

func (a *AndroidAppImport) validatePresignedApk(ctx android.ModuleContext, srcApk android.Path) android.Path {
	stamp := android.PathForModuleOut(ctx, "validated-prebuilt", "check.stamp")
	var extraArgs []string
	if a.Privileged() {
		// It's ok for non-privileged apps to have compressed dex files, see go/gms-uncompressed-jni-slides
		dexCompressionStamp := android.PathForModuleOut(ctx, "validated-prebuilt", "dex_compression.stamp")
		ctx.Build(pctx, android.BuildParams{
			Rule:   checkDexLibsAreUncompressedRule,
			Input:  srcApk,
			Output: dexCompressionStamp,
		})
		validations = append(validations, dexCompressionStamp)
		extraArgs = append(extraArgs, "--privileged")
	}

	ctx.Build(pctx, android.BuildParams{
		Rule:        android.Cp,
		Input:       srcApk,
		Output:      dstApk,
		Validations: validations,
	})
	if proptools.Bool(a.properties.Skip_preprocessed_apk_checks) {
		extraArgs = append(extraArgs, "--skip-preprocessed-apk-checks")
	}
	if proptools.Bool(a.properties.Preprocessed) {
		extraArgs = append(extraArgs, "--preprocessed")
	}

func (a *AndroidAppImport) validateTargetSdkLessThan30(ctx android.ModuleContext, srcApk android.Path) android.Path {
	alignmentStamp := android.PathForModuleOut(ctx, "validated-prebuilt", "old_target_sdk.stamp")
	ctx.Build(pctx, android.BuildParams{
		Rule:   checkBelowTargetSdk30ForNonPreprocessedApks,
		Rule:   checkPresignedApkRule,
		Input:  srcApk,
		Output: alignmentStamp,
		Output: stamp,
		Args: map[string]string{
			"extraArgs": strings.Join(extraArgs, " "),
		},
	})
	return alignmentStamp
	return stamp
}

func (a *AndroidAppImport) Prebuilt() *android.Prebuilt {
+8 −3
Original line number Diff line number Diff line
@@ -659,14 +659,19 @@ func TestAndroidAppImport_Preprocessed(t *testing.T) {

	apkName := "foo.apk"
	variant := ctx.ModuleForTests("foo", "android_common")
	outputBuildParams := variant.Output("validated-prebuilt/" + apkName).BuildParams
	outputBuildParams := variant.Output(apkName).BuildParams
	if outputBuildParams.Rule.String() != android.Cp.String() {
		t.Errorf("Unexpected prebuilt android_app_import rule: " + outputBuildParams.Rule.String())
	}

	// Make sure compression and aligning were validated.
	if len(outputBuildParams.Validations) != 2 {
		t.Errorf("Expected compression/alignment validation rules, found %d validations", len(outputBuildParams.Validations))
	if outputBuildParams.Validation == nil {
		t.Errorf("Expected validation rule, but was not found")
	}

	validationBuildParams := variant.Output("validated-prebuilt/check.stamp").BuildParams
	if validationBuildParams.Rule.String() != checkPresignedApkRule.String() {
		t.Errorf("Unexpected validation rule: " + validationBuildParams.Rule.String())
	}
}

+0 −21
Original line number Diff line number Diff line
@@ -259,32 +259,11 @@ var (
		},
	)

	checkZipAlignment = pctx.AndroidStaticRule("checkzipalign",
		blueprint.RuleParams{
			Command: "if ! ${config.ZipAlign} -c -p 4 $in > /dev/null; then " +
				"echo $in: Improper package alignment >&2; " +
				"exit 1; " +
				"else " +
				"touch $out; " +
				"fi",
			CommandDeps: []string{"${config.ZipAlign}"},
			Description: "Check zip alignment",
		},
	)

	convertImplementationJarToHeaderJarRule = pctx.AndroidStaticRule("convertImplementationJarToHeaderJar",
		blueprint.RuleParams{
			Command:     `${config.Zip2ZipCmd} -i ${in} -o ${out} -x 'META-INF/services/**/*'`,
			CommandDeps: []string{"${config.Zip2ZipCmd}"},
		})

	checkBelowTargetSdk30ForNonPreprocessedApks = pctx.AndroidStaticRule("checkBelowTargetSdk30ForNonPreprocessedApks",
		blueprint.RuleParams{
			Command:     "build/soong/scripts/check_target_sdk_less_than_30.py ${config.Aapt2Cmd} $in $out",
			CommandDeps: []string{"build/soong/scripts/check_target_sdk_less_than_30.py", "${config.Aapt2Cmd}"},
			Description: "Check prebuilt target sdk version",
		},
	)
)

func init() {
+70 −0
Original line number Diff line number Diff line
@@ -4,14 +4,11 @@ import subprocess
import argparse
import re
import sys
import zipfile

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('aapt2', help = "the path to the aapt2 executable")
    parser.add_argument('apk', help = "the apk to check")
    parser.add_argument('stampfile', help = "a file to touch if successful")
    args = parser.parse_args()

def check_target_sdk_less_than_30(args):
    if not args.aapt2:
        sys.exit('--aapt2 is required')
    regex = re.compile(r"targetSdkVersion: *'([0-9]+)'")
    output = subprocess.check_output([args.aapt2, "dump", "badging", args.apk], text=True)
    targetSdkVersion = None
@@ -24,6 +21,49 @@ def main():
    if targetSdkVersion is None or targetSdkVersion >= 30:
        sys.exit(args.apk + ": Prebuilt, presigned apks with targetSdkVersion >= 30 (or a codename targetSdkVersion) must set preprocessed: true in the Android.bp definition (because they must be signed with signature v2, and the build system would wreck that signature otherwise)")

def has_preprocessed_issues(args, *, fail=False):
    if not args.zipalign:
        sys.exit('--zipalign is required')
    ret = subprocess.run([args.zipalign, '-c', '-p', '4', args.apk], stdout=subprocess.DEVNULL).returncode
    if ret != 0:
        if fail:
            sys.exit(args.apk + ': Improper zip alignment')
        return True

    with zipfile.ZipFile(args.apk) as zf:
        for info in zf.infolist():
            if info.filename.startswith('lib/') and info.filename.endswith('.so') and info.compress_type != zipfile.ZIP_STORED:
                if fail:
                    sys.exit(args.apk + ': Contains compressed JNI libraries')
                return True
            # It's ok for non-privileged apps to have compressed dex files, see go/gms-uncompressed-jni-slides
            if args.privileged:
                if info.filename.endswith('.dex') and info.compress_type != zipfile.ZIP_STORED:
                    if fail:
                        sys.exit(args.apk + ': Contains compressed dex files and is privileged')
                    return True
    return False


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('--aapt2', help = "the path to the aapt2 executable")
    parser.add_argument('--zipalign', help = "the path to the zipalign executable")
    parser.add_argument('--skip-preprocessed-apk-checks', action = 'store_true', help = "the value of the soong property with the same name")
    parser.add_argument('--preprocessed', action = 'store_true', help = "the value of the soong property with the same name")
    parser.add_argument('--privileged', action = 'store_true', help = "the value of the soong property with the same name")
    parser.add_argument('apk', help = "the apk to check")
    parser.add_argument('stampfile', help = "a file to touch if successful")
    args = parser.parse_args()

    if not args.preprocessed:
        check_target_sdk_less_than_30(args)
    elif args.skip_preprocessed_apk_checks:
        if not has_preprocessed_issues(args):
            sys.exit('This module sets `skip_preprocessed_apk_checks: true`, but does not actually have any issues. Please remove `skip_preprocessed_apk_checks`.')
    else:
        has_preprocessed_issues(args, fail=True)

    subprocess.check_call(["touch", args.stampfile])

if __name__ == "__main__":