1 /*
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 
21 package android.support.multidex;
22 
23 import java.io.File;
24 import java.io.IOException;
25 import java.io.RandomAccessFile;
26 import java.util.zip.CRC32;
27 import java.util.zip.ZipException;
28 
29 /**
30  * Tools to build a quick partial crc of zip files.
31  */
32 final 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      */
getZipCrc(File apk)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 */
findCentralDirectory(RandomAccessFile raf)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 */
computeCrcOfCentralDir(RandomAccessFile raf, CentralDirectory dir)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 }
126