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