MultiDexExtractor.java revision a0c1a85f60a44ef1c0592821270c20e988c64370
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 22667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chuimport java.io.Closeable; 23667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chuimport java.io.File; 24d9eda5550540306f2037e2db2aba2919fda90057Yohann Rousselimport java.io.FileFilter; 25667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chuimport java.io.FileNotFoundException; 26667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chuimport java.io.FileOutputStream; 27667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chuimport java.io.IOException; 28667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chuimport java.io.InputStream; 29a0c1a85f60a44ef1c0592821270c20e988c64370Maurice Chuimport java.nio.channels.FileLock; 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; 55a0c1a85f60a44ef1c0592821270c20e988c64370Maurice Chu private static final String LOCK_FILENAME = "renamelock"; 56667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu 57667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu private static final int BUFFER_SIZE = 0x4000; 58667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu 59667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu /** 60667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu * Extracts application secondary dexes into files in the application data 61667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu * directory. 62667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu * 63667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu * @return a list of files that were created. The list may be empty if there 64667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu * are no secondary dex files. 65667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu * @throws IOException if encounters a problem while reading or writing 66667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu * secondary dex files 67667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu */ 6852eafa01b1e61e410cb4c5609eacee93c2a3e853Yohann Roussel static List<File> load(ApplicationInfo applicationInfo, File dexDir) 69667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu throws IOException { 70667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu 71d9eda5550540306f2037e2db2aba2919fda90057Yohann Roussel File sourceApk = new File(applicationInfo.sourceDir); 72d9eda5550540306f2037e2db2aba2919fda90057Yohann Roussel long lastModified = sourceApk.lastModified(); 73d9eda5550540306f2037e2db2aba2919fda90057Yohann Roussel String extractedFilePrefix = sourceApk.getName() 74667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu + EXTRACTED_NAME_EXT; 75667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu 76d9eda5550540306f2037e2db2aba2919fda90057Yohann Roussel prepareDexDir(dexDir, extractedFilePrefix, lastModified); 77667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu 78667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu final List<File> files = new ArrayList<File>(); 79667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu ZipFile apk = new ZipFile(applicationInfo.sourceDir); 80667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu try { 81667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu 82667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu int secondaryNumber = 2; 83667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu 84667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu ZipEntry dexFile = apk.getEntry(DEX_PREFIX + secondaryNumber + DEX_SUFFIX); 85667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu while (dexFile != null) { 86667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu String fileName = extractedFilePrefix + secondaryNumber + EXTRACTED_SUFFIX; 87667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu File extractedFile = new File(dexDir, fileName); 88667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu files.add(extractedFile); 89667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu 90667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu if (!extractedFile.isFile()) { 91f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu int numAttempts = 0; 92f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu boolean isExtractionSuccessful = false; 93f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu while (numAttempts < MAX_EXTRACT_ATTEMPTS && !isExtractionSuccessful) { 94f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu numAttempts++; 95f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu 96f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu // Create a zip file (extractedFile) containing only the secondary dex file 97f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu // (dexFile) from the apk. 98f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu extract(apk, dexFile, extractedFile, extractedFilePrefix, 99f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu lastModified); 100f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu 101f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu // Verify that the extracted file is indeed a zip file. 102f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu isExtractionSuccessful = verifyZipFile(extractedFile); 103f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu 104f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu // Log the sha1 of the extracted zip file 105f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu Log.i(TAG, "Extraction " + (isExtractionSuccessful ? "success" : "failed") + 10648cd040f1cf11d716f909f28ee237df6699a0f3bMaurice Chu " - length " + extractedFile.getAbsolutePath() + ": " + 10748cd040f1cf11d716f909f28ee237df6699a0f3bMaurice Chu extractedFile.length()); 108f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu if (!isExtractionSuccessful) { 109f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu // Delete the extracted file 110f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu extractedFile.delete(); 111f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu } 112f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu } 113f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu if (!isExtractionSuccessful) { 114f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu throw new IOException("Could not create zip file " + 115f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu extractedFile.getAbsolutePath() + " for secondary dex (" + 116f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu secondaryNumber + ")"); 117f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu } 118667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu } 119667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu secondaryNumber++; 120667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu dexFile = apk.getEntry(DEX_PREFIX + secondaryNumber + DEX_SUFFIX); 121667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu } 122667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu } finally { 123667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu try { 124667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu apk.close(); 125667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu } catch (IOException e) { 126667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu Log.w(TAG, "Failed to close resource", e); 127667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu } 128667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu } 129667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu 130667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu return files; 131667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu } 132667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu 133d9eda5550540306f2037e2db2aba2919fda90057Yohann Roussel private static void prepareDexDir(File dexDir, final String extractedFilePrefix, 134d9eda5550540306f2037e2db2aba2919fda90057Yohann Roussel final long sourceLastModified) throws IOException { 135667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu dexDir.mkdir(); 136667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu if (!dexDir.isDirectory()) { 137667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu throw new IOException("Failed to create dex directory " + dexDir.getPath()); 138667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu } 139667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu 140667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu // Clean possible old files 141d9eda5550540306f2037e2db2aba2919fda90057Yohann Roussel FileFilter filter = new FileFilter() { 142d9eda5550540306f2037e2db2aba2919fda90057Yohann Roussel 143667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu @Override 144d9eda5550540306f2037e2db2aba2919fda90057Yohann Roussel public boolean accept(File pathname) { 145d9eda5550540306f2037e2db2aba2919fda90057Yohann Roussel return (!pathname.getName().startsWith(extractedFilePrefix)) 146d9eda5550540306f2037e2db2aba2919fda90057Yohann Roussel || (pathname.lastModified() < sourceLastModified); 147667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu } 148667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu }; 149667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu File[] files = dexDir.listFiles(filter); 150667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu if (files == null) { 151667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu Log.w(TAG, "Failed to list secondary dex dir content (" + dexDir.getPath() + ")."); 152667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu return; 153667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu } 154667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu for (File oldFile : files) { 155667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu if (!oldFile.delete()) { 156667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu Log.w(TAG, "Failed to delete old file " + oldFile.getPath()); 157667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu } 158667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu } 159667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu } 160667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu 16152eafa01b1e61e410cb4c5609eacee93c2a3e853Yohann Roussel private static void extract(ZipFile apk, ZipEntry dexFile, File extractTo, 162d9eda5550540306f2037e2db2aba2919fda90057Yohann Roussel String extractedFilePrefix, long sourceLastModified) 163d9eda5550540306f2037e2db2aba2919fda90057Yohann Roussel throws IOException, FileNotFoundException { 164667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu 165667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu InputStream in = apk.getInputStream(dexFile); 166667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu ZipOutputStream out = null; 167667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu File tmp = File.createTempFile(extractedFilePrefix, EXTRACTED_SUFFIX, 168667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu extractTo.getParentFile()); 169667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu Log.i(TAG, "Extracting " + tmp.getPath()); 170667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu try { 171667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu out = new ZipOutputStream(new FileOutputStream(tmp)); 172667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu try { 173667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu ZipEntry classesDex = new ZipEntry("classes.dex"); 174edf0717d4203bd7e9c9435019e3c256d564b4583Yohann Roussel // keep zip entry time since it is the criteria used by Dalvik 175edf0717d4203bd7e9c9435019e3c256d564b4583Yohann Roussel classesDex.setTime(dexFile.getTime()); 176667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu out.putNextEntry(classesDex); 177667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu 178667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu byte[] buffer = new byte[BUFFER_SIZE]; 179667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu int length = in.read(buffer); 1801f8c349b6524aa39a10a570115ce0afb039bd06fMaurice Chu while (length != -1) { 18152eafa01b1e61e410cb4c5609eacee93c2a3e853Yohann Roussel out.write(buffer, 0, length); 182667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu length = in.read(buffer); 183667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu } 184667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu } finally { 185a0c1a85f60a44ef1c0592821270c20e988c64370Maurice Chu out.close(); 186667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu } 187d9eda5550540306f2037e2db2aba2919fda90057Yohann Roussel if (!tmp.setLastModified(sourceLastModified)) { 188d9eda5550540306f2037e2db2aba2919fda90057Yohann Roussel Log.e(TAG, "Failed to set time of \"" + tmp.getAbsolutePath() + "\"." + 189d9eda5550540306f2037e2db2aba2919fda90057Yohann Roussel " This may cause problems with later updates of the apk."); 190d9eda5550540306f2037e2db2aba2919fda90057Yohann Roussel } 191667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu Log.i(TAG, "Renaming to " + extractTo.getPath()); 192a0c1a85f60a44ef1c0592821270c20e988c64370Maurice Chu File lockFile = new File(extractTo.getParentFile(), LOCK_FILENAME); 193a0c1a85f60a44ef1c0592821270c20e988c64370Maurice Chu // Grab the file lock. 194a0c1a85f60a44ef1c0592821270c20e988c64370Maurice Chu FileOutputStream lockFileOutputStream = new FileOutputStream(lockFile); 195a0c1a85f60a44ef1c0592821270c20e988c64370Maurice Chu FileLock lockFileLock = lockFileOutputStream.getChannel().lock(); 196a0c1a85f60a44ef1c0592821270c20e988c64370Maurice Chu try { 197a0c1a85f60a44ef1c0592821270c20e988c64370Maurice Chu if (!extractTo.exists()) { 198a0c1a85f60a44ef1c0592821270c20e988c64370Maurice Chu if (!tmp.renameTo(extractTo)) { 199a0c1a85f60a44ef1c0592821270c20e988c64370Maurice Chu throw new IOException("Failed to rename \"" + tmp.getAbsolutePath() + 200a0c1a85f60a44ef1c0592821270c20e988c64370Maurice Chu "\" to \"" + extractTo.getAbsolutePath() + "\""); 201a0c1a85f60a44ef1c0592821270c20e988c64370Maurice Chu } 202a0c1a85f60a44ef1c0592821270c20e988c64370Maurice Chu } 203a0c1a85f60a44ef1c0592821270c20e988c64370Maurice Chu } finally { 204a0c1a85f60a44ef1c0592821270c20e988c64370Maurice Chu // Release the lock file. 205a0c1a85f60a44ef1c0592821270c20e988c64370Maurice Chu lockFileLock.release(); 206a0c1a85f60a44ef1c0592821270c20e988c64370Maurice Chu lockFileOutputStream.close(); 207667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu } 208667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu } finally { 209667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu closeQuietly(in); 210667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu tmp.delete(); // return status ignored 211667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu } 212667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu } 213667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu 214667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu /** 215f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu * Returns whether the file is a valid zip file. 216f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu */ 217f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu private static boolean verifyZipFile(File file) { 218f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu try { 219f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu ZipFile zipFile = new ZipFile(file); 220f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu try { 221f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu zipFile.close(); 222a0c1a85f60a44ef1c0592821270c20e988c64370Maurice Chu return true; 223f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu } catch (IOException e) { 224f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu Log.w(TAG, "Failed to close zip file: " + file.getAbsolutePath()); 225f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu } 226f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu } catch (ZipException ex) { 227f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu Log.w(TAG, "File " + file.getAbsolutePath() + " is not a valid zip file.", ex); 228f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu } catch (IOException ex) { 229f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu Log.w(TAG, "Got an IOException trying to open zip file: " + file.getAbsolutePath(), ex); 230f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu } 231f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu return false; 232f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu } 233f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu 234f6d1f23926672c8dd61da515f8d1bcb37ef4292dMaurice Chu /** 235667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu * Closes the given {@code Closeable}. Suppresses any IO exceptions. 236667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu */ 237667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu private static void closeQuietly(Closeable closeable) { 238667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu try { 239667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu closeable.close(); 240667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu } catch (IOException e) { 241667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu Log.w(TAG, "Failed to close resource", e); 242667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu } 243667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu } 244667f9a8a8155c41970a83be1414b57b5e37de336Maurice Chu} 245