MultiDexExtractor.java revision cc63eda4e6defe6b0dd5dd3c8fa608cf6ff26011
1667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu/* 2667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu * Copyright (C) 2013 The Android Open Source Project 3667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu * 4667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu * Licensed under the Apache License, Version 2.0 (the "License"); 5667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu * you may not use this file except in compliance with the License. 6667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu * You may obtain a copy of the License at 7667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu * 8667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu * http://www.apache.org/licenses/LICENSE-2.0 9667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu * 10667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu * Unless required by applicable law or agreed to in writing, software 11667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu * distributed under the License is distributed on an "AS IS" BASIS, 12667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu * See the License for the specific language governing permissions and 14667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu * limitations under the License. 15667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu */ 16667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu 17667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chupackage android.support.multidex; 18667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu 191f8c349b6524aa39a10a570115ce0afb039bd06fMaurice Chuimport android.content.pm.ApplicationInfo; 201f8c349b6524aa39a10a570115ce0afb039bd06fMaurice Chuimport android.util.Log; 211f8c349b6524aa39a10a570115ce0afb039bd06fMaurice Chu 22a159fd5dbd60171c9bb602f316ad8272419afe40Maurice Chuimport java.io.BufferedOutputStream; 23667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chuimport java.io.Closeable; 24667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chuimport java.io.File; 25d9eda5550540306f2037e2db2aba2919fda90057Yohann Rousselimport java.io.FileFilter; 26667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chuimport java.io.FileNotFoundException; 27667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chuimport java.io.FileOutputStream; 28667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chuimport java.io.IOException; 29667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chuimport java.io.InputStream; 30667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chuimport java.util.ArrayList; 31667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chuimport java.util.List; 32667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chuimport java.util.zip.ZipEntry; 33f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chuimport java.util.zip.ZipException; 34667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chuimport java.util.zip.ZipFile; 35667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chuimport java.util.zip.ZipOutputStream; 36667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu 37667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu/** 38667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu * Exposes application secondary dex files as files in the application data 39667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu * directory. 40667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu */ 41667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chufinal class MultiDexExtractor { 42667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu 43667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu private static final String TAG = MultiDex.TAG; 44667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu 45667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu /** 46667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu * We look for additional dex files named {@code classes2.dex}, 47667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu * {@code classes3.dex}, etc. 48667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu */ 49667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu private static final String DEX_PREFIX = "classes"; 50667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu private static final String DEX_SUFFIX = ".dex"; 51667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu 52667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu private static final String EXTRACTED_NAME_EXT = ".classes"; 53667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu private static final String EXTRACTED_SUFFIX = ".zip"; 54f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu private static final int MAX_EXTRACT_ATTEMPTS = 3; 55667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu 56667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu private static final int BUFFER_SIZE = 0x4000; 57667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu 58667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu /** 59667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu * Extracts application secondary dexes into files in the application data 60667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu * directory. 61667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu * 62667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu * @return a list of files that were created. The list may be empty if there 63667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu * are no secondary dex files. 64667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu * @throws IOException if encounters a problem while reading or writing 65667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu * secondary dex files 66667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu */ 67cc63eda4e6defe6b0dd5dd3c8fa608cf6ff26011Maurice Chu static List<File> load(ApplicationInfo applicationInfo, File dexDir, boolean forceReload) 68667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu throws IOException { 69cc63eda4e6defe6b0dd5dd3c8fa608cf6ff26011Maurice Chu Log.i(TAG, "load(" + applicationInfo.sourceDir + ", forceReload=" + forceReload + ")"); 70d9eda5550540306f2037e2db2aba2919fda90057Yohann Roussel File sourceApk = new File(applicationInfo.sourceDir); 71d9eda5550540306f2037e2db2aba2919fda90057Yohann Roussel long lastModified = sourceApk.lastModified(); 72d9eda5550540306f2037e2db2aba2919fda90057Yohann Roussel String extractedFilePrefix = sourceApk.getName() 73667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu + EXTRACTED_NAME_EXT; 74667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu 75d9eda5550540306f2037e2db2aba2919fda90057Yohann Roussel prepareDexDir(dexDir, extractedFilePrefix, lastModified); 76667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu 77667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu final List<File> files = new ArrayList<File>(); 78667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu ZipFile apk = new ZipFile(applicationInfo.sourceDir); 79667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu try { 80667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu 81667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu int secondaryNumber = 2; 82667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu 83667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu ZipEntry dexFile = apk.getEntry(DEX_PREFIX + secondaryNumber + DEX_SUFFIX); 84667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu while (dexFile != null) { 85667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu String fileName = extractedFilePrefix + secondaryNumber + EXTRACTED_SUFFIX; 86667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu File extractedFile = new File(dexDir, fileName); 87667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu files.add(extractedFile); 88667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu 8988117c37e2c3f8601f295b74a3e804877afb78eeYohann Roussel Log.i(TAG, "Need extracted file " + extractedFile); 90cc63eda4e6defe6b0dd5dd3c8fa608cf6ff26011Maurice Chu if (forceReload || !extractedFile.isFile()) { 9188117c37e2c3f8601f295b74a3e804877afb78eeYohann Roussel Log.i(TAG, "Extraction is needed for file " + extractedFile); 92f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu int numAttempts = 0; 93f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu boolean isExtractionSuccessful = false; 94f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu while (numAttempts < MAX_EXTRACT_ATTEMPTS && !isExtractionSuccessful) { 95f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu numAttempts++; 96f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu 97f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu // Create a zip file (extractedFile) containing only the secondary dex file 98f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu // (dexFile) from the apk. 99f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu extract(apk, dexFile, extractedFile, extractedFilePrefix, 100f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu lastModified); 101f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu 102f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu // Verify that the extracted file is indeed a zip file. 103f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu isExtractionSuccessful = verifyZipFile(extractedFile); 104f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu 105f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu // Log the sha1 of the extracted zip file 106f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu Log.i(TAG, "Extraction " + (isExtractionSuccessful ? "success" : "failed") + 10748cd040f1cf11d716f909f28ee237df6699a0f3bMaurice Chu " - length " + extractedFile.getAbsolutePath() + ": " + 10848cd040f1cf11d716f909f28ee237df6699a0f3bMaurice Chu extractedFile.length()); 109f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu if (!isExtractionSuccessful) { 110f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu // Delete the extracted file 111f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu extractedFile.delete(); 112f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu } 113f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu } 114f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu if (!isExtractionSuccessful) { 115f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu throw new IOException("Could not create zip file " + 116f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu extractedFile.getAbsolutePath() + " for secondary dex (" + 117f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu secondaryNumber + ")"); 118f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu } 11988117c37e2c3f8601f295b74a3e804877afb78eeYohann Roussel } else { 12088117c37e2c3f8601f295b74a3e804877afb78eeYohann Roussel Log.i(TAG, "No extraction needed for " + extractedFile + " of size " + extractedFile.length()); 121667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu } 122667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu secondaryNumber++; 123667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu dexFile = apk.getEntry(DEX_PREFIX + secondaryNumber + DEX_SUFFIX); 124667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu } 125667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu } finally { 126667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu try { 127667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu apk.close(); 128667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu } catch (IOException e) { 129667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu Log.w(TAG, "Failed to close resource", e); 130667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu } 131667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu } 132667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu 133667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu return files; 134667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu } 135667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu 136cc63eda4e6defe6b0dd5dd3c8fa608cf6ff26011Maurice Chu /** 137cc63eda4e6defe6b0dd5dd3c8fa608cf6ff26011Maurice Chu * This removes any old zip and dex files or extraneous files from the secondary-dexes 138cc63eda4e6defe6b0dd5dd3c8fa608cf6ff26011Maurice Chu * directory. 139cc63eda4e6defe6b0dd5dd3c8fa608cf6ff26011Maurice Chu */ 140d9eda5550540306f2037e2db2aba2919fda90057Yohann Roussel private static void prepareDexDir(File dexDir, final String extractedFilePrefix, 141d9eda5550540306f2037e2db2aba2919fda90057Yohann Roussel final long sourceLastModified) throws IOException { 142667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu dexDir.mkdir(); 143667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu if (!dexDir.isDirectory()) { 144667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu throw new IOException("Failed to create dex directory " + dexDir.getPath()); 145667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu } 146667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu 147667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu // Clean possible old files 148d9eda5550540306f2037e2db2aba2919fda90057Yohann Roussel FileFilter filter = new FileFilter() { 149d9eda5550540306f2037e2db2aba2919fda90057Yohann Roussel 150667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu @Override 151d9eda5550540306f2037e2db2aba2919fda90057Yohann Roussel public boolean accept(File pathname) { 152d9eda5550540306f2037e2db2aba2919fda90057Yohann Roussel return (!pathname.getName().startsWith(extractedFilePrefix)) 153cc63eda4e6defe6b0dd5dd3c8fa608cf6ff26011Maurice Chu || (pathname.lastModified() < sourceLastModified); 154667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu } 155667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu }; 156667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu File[] files = dexDir.listFiles(filter); 157667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu if (files == null) { 158667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu Log.w(TAG, "Failed to list secondary dex dir content (" + dexDir.getPath() + ")."); 159667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu return; 160667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu } 161667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu for (File oldFile : files) { 16288117c37e2c3f8601f295b74a3e804877afb78eeYohann Roussel Log.w(TAG, "Trying to delete old file " + oldFile.getPath() + " of size " + oldFile.length()); 163667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu if (!oldFile.delete()) { 164667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu Log.w(TAG, "Failed to delete old file " + oldFile.getPath()); 16588117c37e2c3f8601f295b74a3e804877afb78eeYohann Roussel } else { 16688117c37e2c3f8601f295b74a3e804877afb78eeYohann Roussel Log.w(TAG, "Deleted old file " + oldFile.getPath()); 167667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu } 168667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu } 169667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu } 170667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu 17152eafa01b1e61e410cb4c5609eacee93c2a3e853Yohann Roussel private static void extract(ZipFile apk, ZipEntry dexFile, File extractTo, 172d9eda5550540306f2037e2db2aba2919fda90057Yohann Roussel String extractedFilePrefix, long sourceLastModified) 173d9eda5550540306f2037e2db2aba2919fda90057Yohann Roussel throws IOException, FileNotFoundException { 174667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu 175667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu InputStream in = apk.getInputStream(dexFile); 176667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu ZipOutputStream out = null; 177667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu File tmp = File.createTempFile(extractedFilePrefix, EXTRACTED_SUFFIX, 178667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu extractTo.getParentFile()); 179667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu Log.i(TAG, "Extracting " + tmp.getPath()); 180667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu try { 181a159fd5dbd60171c9bb602f316ad8272419afe40Maurice Chu out = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(tmp))); 182667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu try { 183667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu ZipEntry classesDex = new ZipEntry("classes.dex"); 184edf0717d4203bd7e9c9435019e3c256d564b4583Yohann Roussel // keep zip entry time since it is the criteria used by Dalvik 185edf0717d4203bd7e9c9435019e3c256d564b4583Yohann Roussel classesDex.setTime(dexFile.getTime()); 186667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu out.putNextEntry(classesDex); 187667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu 188667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu byte[] buffer = new byte[BUFFER_SIZE]; 189667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu int length = in.read(buffer); 1901f8c349b6524aa39a10a570115ce0afb039bd06fMaurice Chu while (length != -1) { 19152eafa01b1e61e410cb4c5609eacee93c2a3e853Yohann Roussel out.write(buffer, 0, length); 192667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu length = in.read(buffer); 193667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu } 194a159fd5dbd60171c9bb602f316ad8272419afe40Maurice Chu out.closeEntry(); 195667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu } finally { 196a0c1a85f60a44ef1c0592821270c20e988c64370Maurice Chu out.close(); 197667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu } 198d9eda5550540306f2037e2db2aba2919fda90057Yohann Roussel if (!tmp.setLastModified(sourceLastModified)) { 199d9eda5550540306f2037e2db2aba2919fda90057Yohann Roussel Log.e(TAG, "Failed to set time of \"" + tmp.getAbsolutePath() + "\"." + 200d9eda5550540306f2037e2db2aba2919fda90057Yohann Roussel " This may cause problems with later updates of the apk."); 201d9eda5550540306f2037e2db2aba2919fda90057Yohann Roussel } 202667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu Log.i(TAG, "Renaming to " + extractTo.getPath()); 203a159fd5dbd60171c9bb602f316ad8272419afe40Maurice Chu if (!tmp.renameTo(extractTo)) { 204a159fd5dbd60171c9bb602f316ad8272419afe40Maurice Chu throw new IOException("Failed to rename \"" + tmp.getAbsolutePath() + 205a159fd5dbd60171c9bb602f316ad8272419afe40Maurice Chu "\" to \"" + extractTo.getAbsolutePath() + "\""); 206667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu } 207667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu } finally { 208667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu closeQuietly(in); 209667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu tmp.delete(); // return status ignored 210667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu } 211667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu } 212667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu 213667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu /** 214f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu * Returns whether the file is a valid zip file. 215f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu */ 216cc63eda4e6defe6b0dd5dd3c8fa608cf6ff26011Maurice Chu static boolean verifyZipFile(File file) { 217f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu try { 218f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu ZipFile zipFile = new ZipFile(file); 219f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu try { 220f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu zipFile.close(); 221a0c1a85f60a44ef1c0592821270c20e988c64370Maurice Chu return true; 222f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu } catch (IOException e) { 223f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu Log.w(TAG, "Failed to close zip file: " + file.getAbsolutePath()); 224f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu } 225f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu } catch (ZipException ex) { 226f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu Log.w(TAG, "File " + file.getAbsolutePath() + " is not a valid zip file.", ex); 227f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu } catch (IOException ex) { 228f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu Log.w(TAG, "Got an IOException trying to open zip file: " + file.getAbsolutePath(), ex); 229f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu } 230f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu return false; 231f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu } 232f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu 233f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu /** 234667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu * Closes the given {@code Closeable}. Suppresses any IO exceptions. 235667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu */ 236667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu private static void closeQuietly(Closeable closeable) { 237667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu try { 238667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu closeable.close(); 239667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu } catch (IOException e) { 240667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu Log.w(TAG, "Failed to close resource", e); 241667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu } 242667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu } 243667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu} 244