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
This commit is contained in:
Doug Zongker 2014-05-15 09:54:26 -07:00
parent 944a38104a
commit 1d67eec191
1 changed files with 66 additions and 10 deletions

View File

@ -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) {
// Preserve the STORED method of the input entry.
outEntry = new JarEntry(inEntry);
} else {
// Create a new entry so that the compressed len is recomputed.
outEntry = new JarEntry(name);
if (inEntry.getMethod() != JarEntry.STORED) continue;
// Preserve the STORED method of the input entry.
outEntry = new JarEntry(inEntry);
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();
}