/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* Apache Harmony HEADER because the code in this class comes mostly from ZipFile, ZipEntry and * ZipConstants from android libcore. */ package android.support.multidex; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.util.zip.CRC32; import java.util.zip.ZipException; /** * Tools to build a quick partial crc of zip files. */ final class ZipUtil { static class CentralDirectory { long offset; long size; } /* redefine those constant here because of bug 13721174 preventing to compile using the * constants defined in ZipFile */ private static final int ENDHDR = 22; private static final int ENDSIG = 0x6054b50; /** * Size of reading buffers. */ private static final int BUFFER_SIZE = 0x4000; /** * Compute crc32 of the central directory of an apk. The central directory contains * the crc32 of each entries in the zip so the computed result is considered valid for the whole * zip file. Does not support zip64 nor multidisk but it should be OK for now since ZipFile does * not either. */ static long getZipCrc(File apk) throws IOException { RandomAccessFile raf = new RandomAccessFile(apk, "r"); try { CentralDirectory dir = findCentralDirectory(raf); return computeCrcOfCentralDir(raf, dir); } finally { raf.close(); } } /* Package visible for testing */ static CentralDirectory findCentralDirectory(RandomAccessFile raf) throws IOException, ZipException { long scanOffset = raf.length() - ENDHDR; if (scanOffset < 0) { throw new ZipException("File too short to be a zip file: " + raf.length()); } long stopOffset = scanOffset - 0x10000 /* ".ZIP file comment"'s max length */; if (stopOffset < 0) { stopOffset = 0; } int endSig = Integer.reverseBytes(ENDSIG); while (true) { raf.seek(scanOffset); if (raf.readInt() == endSig) { break; } scanOffset--; if (scanOffset < stopOffset) { throw new ZipException("End Of Central Directory signature not found"); } } // Read the End Of Central Directory. ENDHDR includes the signature // bytes, // which we've already read. // Pull out the information we need. raf.skipBytes(2); // diskNumber raf.skipBytes(2); // diskWithCentralDir raf.skipBytes(2); // numEntries raf.skipBytes(2); // totalNumEntries CentralDirectory dir = new CentralDirectory(); dir.size = Integer.reverseBytes(raf.readInt()) & 0xFFFFFFFFL; dir.offset = Integer.reverseBytes(raf.readInt()) & 0xFFFFFFFFL; return dir; } /* Package visible for testing */ static long computeCrcOfCentralDir(RandomAccessFile raf, CentralDirectory dir) throws IOException { CRC32 crc = new CRC32(); long stillToRead = dir.size; raf.seek(dir.offset); int length = (int) Math.min(BUFFER_SIZE, stillToRead); byte[] buffer = new byte[BUFFER_SIZE]; length = raf.read(buffer, 0, length); while (length != -1) { crc.update(buffer, 0, length); stillToRead -= length; if (stillToRead == 0) { break; } length = (int) Math.min(BUFFER_SIZE, stillToRead); length = raf.read(buffer, 0, length); } return crc.getValue(); } }