blob: cd518ccf4e36b114635986b1f5c486da7aed07ad [file] [log] [blame]
Yohann Roussel602c6ca2014-03-28 17:35:02 +01001/*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17/* Apache Harmony HEADER because the code in this class comes mostly from ZipFile, ZipEntry and
18 * ZipConstants from android libcore.
19 */
20
21package android.support.multidex;
22
23import java.io.File;
24import java.io.IOException;
25import java.io.RandomAccessFile;
26import java.util.zip.CRC32;
27import java.util.zip.ZipException;
28
29/**
30 * Tools to build a quick partial crc of zip files.
31 */
32final class ZipUtil {
33 static class CentralDirectory {
34 long offset;
35 long size;
36 }
37
38 /* redefine those constant here because of bug 13721174 preventing to compile using the
39 * constants defined in ZipFile */
40 private static final int ENDHDR = 22;
41 private static final int ENDSIG = 0x6054b50;
42
43 /**
44 * Size of reading buffers.
45 */
46 private static final int BUFFER_SIZE = 0x4000;
47
48 /**
49 * Compute crc32 of the central directory of an apk. The central directory contains
50 * the crc32 of each entries in the zip so the computed result is considered valid for the whole
51 * zip file. Does not support zip64 nor multidisk but it should be OK for now since ZipFile does
52 * not either.
53 */
54 static long getZipCrc(File apk) throws IOException {
55 RandomAccessFile raf = new RandomAccessFile(apk, "r");
56 try {
57 CentralDirectory dir = findCentralDirectory(raf);
58
59 return computeCrcOfCentralDir(raf, dir);
60 } finally {
61 raf.close();
62 }
63 }
64
65 /* Package visible for testing */
66 static CentralDirectory findCentralDirectory(RandomAccessFile raf) throws IOException,
67 ZipException {
68 long scanOffset = raf.length() - ENDHDR;
69 if (scanOffset < 0) {
70 throw new ZipException("File too short to be a zip file: " + raf.length());
71 }
72
73 long stopOffset = scanOffset - 0x10000 /* ".ZIP file comment"'s max length */;
74 if (stopOffset < 0) {
75 stopOffset = 0;
76 }
77
78 int endSig = Integer.reverseBytes(ENDSIG);
79 while (true) {
80 raf.seek(scanOffset);
81 if (raf.readInt() == endSig) {
82 break;
83 }
84
85 scanOffset--;
86 if (scanOffset < stopOffset) {
87 throw new ZipException("End Of Central Directory signature not found");
88 }
89 }
90 // Read the End Of Central Directory. ENDHDR includes the signature
91 // bytes,
92 // which we've already read.
93
94 // Pull out the information we need.
95 raf.skipBytes(2); // diskNumber
96 raf.skipBytes(2); // diskWithCentralDir
97 raf.skipBytes(2); // numEntries
98 raf.skipBytes(2); // totalNumEntries
99 CentralDirectory dir = new CentralDirectory();
100 dir.size = Integer.reverseBytes(raf.readInt()) & 0xFFFFFFFFL;
101 dir.offset = Integer.reverseBytes(raf.readInt()) & 0xFFFFFFFFL;
102 return dir;
103 }
104
105 /* Package visible for testing */
106 static long computeCrcOfCentralDir(RandomAccessFile raf, CentralDirectory dir)
107 throws IOException {
108 CRC32 crc = new CRC32();
109 long stillToRead = dir.size;
110 raf.seek(dir.offset);
111 int length = (int) Math.min(BUFFER_SIZE, stillToRead);
112 byte[] buffer = new byte[BUFFER_SIZE];
113 length = raf.read(buffer, 0, length);
114 while (length != -1) {
115 crc.update(buffer, 0, length);
116 stillToRead -= length;
117 if (stillToRead == 0) {
118 break;
119 }
120 length = (int) Math.min(BUFFER_SIZE, stillToRead);
121 length = raf.read(buffer, 0, length);
122 }
123 return crc.getValue();
124 }
125}