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

Commit 1d67eec1 authored by Doug Zongker's avatar Doug Zongker
Browse files

make SignApk do zip alignment

When signing an APK, make the SignApk tool align the stored entries to
(by default) 4-byte boundaries.  This obviates the need to run the
separate zipalign tool, which currently does this job.

The alignment byte count can be specified with the -a option.  OTA
package signing (with -w) never does alignment.

The order of files in the output APK is changed so that all stored
files come first in the output, followed by all non-stored files.
This is not expected to have any impact in practice.

Change-Id: Iaeef89b2a7283e25fadb99c0a0f0641f682d76b8
parent 944a3810
Loading
Loading
Loading
Loading
+66 −10
Original line number Diff line number Diff line
@@ -461,24 +461,75 @@ class SignApk {
     * reduce variation in the output file and make incremental OTAs
     * more efficient.
     */
    private static void copyFiles(Manifest manifest,
                                  JarFile in, JarOutputStream out, long timestamp) throws IOException {
    private static void copyFiles(Manifest manifest, JarFile in, JarOutputStream out,
                                  long timestamp, int alignment) throws IOException {
        byte[] buffer = new byte[4096];
        int num;

        Map<String, Attributes> entries = manifest.getEntries();
        ArrayList<String> names = new ArrayList<String>(entries.keySet());
        Collections.sort(names);

        boolean firstEntry = true;
        long offset = 0L;

        // We do the copy in two passes -- first copying all the
        // entries that are STORED, then copying all the entries that
        // have any other compression flag (which in practice means
        // DEFLATED).  This groups all the stored entries together at
        // the start of the file and makes it easier to do alignment
        // on them (since only stored entries are aligned).

        for (String name : names) {
            JarEntry inEntry = in.getJarEntry(name);
            JarEntry outEntry = null;
            if (inEntry.getMethod() == JarEntry.STORED) {
            if (inEntry.getMethod() != JarEntry.STORED) continue;
            // Preserve the STORED method of the input entry.
            outEntry = new JarEntry(inEntry);
            } else {
            outEntry.setTime(timestamp);

            // 'offset' is the offset into the file at which we expect
            // the file data to begin.  This is the value we need to
            // make a multiple of 'alignement'.
            offset += JarFile.LOCHDR + outEntry.getName().length();
            if (firstEntry) {
                // The first entry in a jar file has an extra field of
                // four bytes that you can't get rid of; any extra
                // data you specify in the JarEntry is appended to
                // these forced four bytes.  This is JAR_MAGIC in
                // JarOutputStream; the bytes are 0xfeca0000.
                offset += 4;
                firstEntry = false;
            }
            if (alignment > 0 && (offset % alignment != 0)) {
                // Set the "extra data" of the entry to between 1 and
                // alignment-1 bytes, to make the file data begin at
                // an aligned offset.
                int needed = alignment - (int)(offset % alignment);
                outEntry.setExtra(new byte[needed]);
                offset += needed;
            }

            out.putNextEntry(outEntry);

            InputStream data = in.getInputStream(inEntry);
            while ((num = data.read(buffer)) > 0) {
                out.write(buffer, 0, num);
                offset += num;
            }
            out.flush();
        }

        // Copy all the non-STORED entries.  We don't attempt to
        // maintain the 'offset' variable past this point; we don't do
        // alignment on these entries.

        for (String name : names) {
            JarEntry inEntry = in.getJarEntry(name);
            JarEntry outEntry = null;
            if (inEntry.getMethod() == JarEntry.STORED) continue;
            // Create a new entry so that the compressed len is recomputed.
            outEntry = new JarEntry(name);
            }
            outEntry.setTime(timestamp);
            out.putNextEntry(outEntry);

@@ -589,7 +640,7 @@ class SignApk {
                long timestamp = publicKey.getNotBefore().getTime() + 3600L * 1000;

                Manifest manifest = addDigestsToManifest(inputJar, hash);
                copyFiles(manifest, inputJar, outputJar, timestamp);
                copyFiles(manifest, inputJar, outputJar, timestamp, 0);
                addOtacert(outputJar, publicKeyFile, timestamp, manifest, hash);

                signFile(manifest, inputJar,
@@ -778,6 +829,7 @@ class SignApk {

    private static void usage() {
        System.err.println("Usage: signapk [-w] " +
                           "[-a <alignment>] " +
                           "[-providerClass <className>] " +
                           "publickey.x509[.pem] privatekey.pk8 " +
                           "[publickey2.x509[.pem] privatekey2.pk8 ...] " +
@@ -794,6 +846,7 @@ class SignApk {
        boolean signWholeFile = false;
        String providerClass = null;
        String providerArg = null;
        int alignment = 4;

        int argstart = 0;
        while (argstart < args.length && args[argstart].startsWith("-")) {
@@ -806,6 +859,9 @@ class SignApk {
                }
                providerClass = args[++argstart];
                ++argstart;
            } else if ("-a".equals(args[argstart])) {
                alignment = Integer.parseInt(args[++argstart]);
                ++argstart;
            } else {
                usage();
            }
@@ -872,7 +928,7 @@ class SignApk {
                outputJar.setLevel(9);

                Manifest manifest = addDigestsToManifest(inputJar, hashes);
                copyFiles(manifest, inputJar, outputJar, timestamp);
                copyFiles(manifest, inputJar, outputJar, timestamp, alignment);
                signFile(manifest, inputJar, publicKey, privateKey, outputJar);
                outputJar.close();
            }