diff options
author | Andrew Johnson <aqj@google.com> | 2016-07-26 09:17:41 +0000 |
---|---|---|
committer | android-build-merger <android-build-merger@google.com> | 2016-07-26 09:17:41 +0000 |
commit | c094a2de01995fbed174287e2c9ad0c0415f5ece (patch) | |
tree | 0ffa33a56c249df4c64e94f5cd3240d1056dcd35 | |
parent | 9ee9a9bd9d8fa602d01567e5c7f00f107ce81019 (diff) | |
parent | d89431ead31219eccb6f261978932a0de3c537d8 (diff) | |
download | multidex-nougat-mr1-security-release.tar.gz |
Prevent concurrent extractionsandroid-cts_7.1_r1android-cts-7.1_r9android-cts-7.1_r8android-cts-7.1_r7android-cts-7.1_r6android-cts-7.1_r5android-cts-7.1_r4android-cts-7.1_r3android-cts-7.1_r29android-cts-7.1_r28android-cts-7.1_r27android-cts-7.1_r26android-cts-7.1_r25android-cts-7.1_r24android-cts-7.1_r23android-cts-7.1_r22android-cts-7.1_r21android-cts-7.1_r20android-cts-7.1_r2android-cts-7.1_r19android-cts-7.1_r18android-cts-7.1_r17android-cts-7.1_r16android-cts-7.1_r15android-cts-7.1_r14android-cts-7.1_r13android-cts-7.1_r12android-cts-7.1_r11android-cts-7.1_r10android-cts-7.1_r1android-7.1.2_r9android-7.1.2_r8android-7.1.2_r6android-7.1.2_r5android-7.1.2_r4android-7.1.2_r39android-7.1.2_r38android-7.1.2_r37android-7.1.2_r36android-7.1.2_r33android-7.1.2_r32android-7.1.2_r30android-7.1.2_r3android-7.1.2_r29android-7.1.2_r28android-7.1.2_r27android-7.1.2_r25android-7.1.2_r24android-7.1.2_r23android-7.1.2_r2android-7.1.2_r19android-7.1.2_r18android-7.1.2_r17android-7.1.2_r16android-7.1.2_r15android-7.1.2_r14android-7.1.2_r13android-7.1.2_r12android-7.1.2_r11android-7.1.2_r10android-7.1.2_r1android-7.1.1_r9android-7.1.1_r8android-7.1.1_r7android-7.1.1_r61android-7.1.1_r60android-7.1.1_r6android-7.1.1_r59android-7.1.1_r58android-7.1.1_r57android-7.1.1_r56android-7.1.1_r55android-7.1.1_r54android-7.1.1_r53android-7.1.1_r52android-7.1.1_r51android-7.1.1_r50android-7.1.1_r49android-7.1.1_r48android-7.1.1_r47android-7.1.1_r46android-7.1.1_r45android-7.1.1_r44android-7.1.1_r43android-7.1.1_r42android-7.1.1_r41android-7.1.1_r40android-7.1.1_r4android-7.1.1_r39android-7.1.1_r38android-7.1.1_r35android-7.1.1_r33android-7.1.1_r32android-7.1.1_r31android-7.1.1_r3android-7.1.1_r28android-7.1.1_r27android-7.1.1_r26android-7.1.1_r25android-7.1.1_r24android-7.1.1_r23android-7.1.1_r22android-7.1.1_r21android-7.1.1_r20android-7.1.1_r2android-7.1.1_r17android-7.1.1_r16android-7.1.1_r15android-7.1.1_r14android-7.1.1_r13android-7.1.1_r12android-7.1.1_r11android-7.1.1_r10android-7.1.1_r1android-7.1.0_r7android-7.1.0_r6android-7.1.0_r5android-7.1.0_r4android-7.1.0_r3android-7.1.0_r2android-7.1.0_r1nougat-mr2.3-releasenougat-mr2.2-releasenougat-mr2.1-releasenougat-mr2-security-releasenougat-mr2-releasenougat-mr2-pixel-releasenougat-mr2-devnougat-mr1.8-releasenougat-mr1.7-releasenougat-mr1.6-releasenougat-mr1.5-releasenougat-mr1.4-releasenougat-mr1.3-releasenougat-mr1.2-releasenougat-mr1.1-releasenougat-mr1-volantis-releasenougat-mr1-security-releasenougat-mr1-releasenougat-mr1-flounder-releasenougat-mr1-devnougat-mr1-cts-releasenougat-dr1-release
am: d89431ead3
Change-Id: I8fe1f1aeb0d814aca58dbc9d35089377d8af0a9a
-rw-r--r-- | library/src/android/support/multidex/MultiDexExtractor.java | 123 |
1 files changed, 75 insertions, 48 deletions
diff --git a/library/src/android/support/multidex/MultiDexExtractor.java b/library/src/android/support/multidex/MultiDexExtractor.java index 4aef9f2..32d7ee9 100644 --- a/library/src/android/support/multidex/MultiDexExtractor.java +++ b/library/src/android/support/multidex/MultiDexExtractor.java @@ -30,8 +30,9 @@ import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; +import java.io.RandomAccessFile; +import java.nio.channels.FileChannel; +import java.nio.channels.FileLock; import java.util.ArrayList; import java.util.List; import java.util.zip.ZipEntry; @@ -70,12 +71,14 @@ final class MultiDexExtractor { /* Keep value away from 0 because it is a too probable time stamp value */ private static final long NO_VALUE = -1L; + private static final String LOCK_FILENAME = "MultiDex.lock"; + /** * Extracts application secondary dexes into files in the application data * directory. * * @return a list of files that were created. The list may be empty if there - * are no secondary dex files. + * are no secondary dex files. Never return null. * @throws IOException if encounters a problem while reading or writing * secondary dex files */ @@ -86,27 +89,64 @@ final class MultiDexExtractor { long currentCrc = getZipCrc(sourceApk); + // Validity check and extraction must be done only while the lock file has been taken. + File lockFile = new File(dexDir, LOCK_FILENAME); + RandomAccessFile lockRaf = new RandomAccessFile(lockFile, "rw"); + FileChannel lockChannel = null; + FileLock cacheLock = null; List<File> files; - if (!forceReload && !isModified(context, sourceApk, currentCrc)) { - try { - files = loadExistingExtractions(context, sourceApk, dexDir); - } catch (IOException ioe) { - Log.w(TAG, "Failed to reload existing extracted secondary dex files," - + " falling back to fresh extraction", ioe); + IOException releaseLockException = null; + try { + lockChannel = lockRaf.getChannel(); + Log.i(TAG, "Blocking on lock " + lockFile.getPath()); + cacheLock = lockChannel.lock(); + Log.i(TAG, lockFile.getPath() + " locked"); + + if (!forceReload && !isModified(context, sourceApk, currentCrc)) { + try { + files = loadExistingExtractions(context, sourceApk, dexDir); + } catch (IOException ioe) { + Log.w(TAG, "Failed to reload existing extracted secondary dex files," + + " falling back to fresh extraction", ioe); + files = performExtractions(sourceApk, dexDir); + putStoredApkInfo(context, + getTimeStamp(sourceApk), currentCrc, files.size() + 1); + + } + } else { + Log.i(TAG, "Detected that extraction must be performed."); files = performExtractions(sourceApk, dexDir); putStoredApkInfo(context, getTimeStamp(sourceApk), currentCrc, files.size() + 1); - } - } else { - Log.i(TAG, "Detected that extraction must be performed."); - files = performExtractions(sourceApk, dexDir); - putStoredApkInfo(context, getTimeStamp(sourceApk), currentCrc, files.size() + 1); + } finally { + if (cacheLock != null) { + try { + cacheLock.release(); + } catch (IOException e) { + Log.e(TAG, "Failed to release lock on " + lockFile.getPath()); + // Exception while releasing the lock is bad, we want to report it, but not at + // the price of overriding any already pending exception. + releaseLockException = e; + } + } + if (lockChannel != null) { + closeQuietly(lockChannel); + } + closeQuietly(lockRaf); + } + + if (releaseLockException != null) { + throw releaseLockException; } Log.i(TAG, "load found " + files.size() + " secondary dex files"); return files; } + /** + * Load previously extracted secondary dex files. Should be called only while owning the lock on + * {@link #LOCK_FILENAME}. + */ private static List<File> loadExistingExtractions(Context context, File sourceApk, File dexDir) throws IOException { Log.i(TAG, "loading existing secondary dex files"); @@ -133,6 +173,11 @@ final class MultiDexExtractor { return files; } + + /** + * Compare current archive and crc with values stored in {@link SharedPreferences}. Should be + * called only while owning the lock on {@link #LOCK_FILENAME}. + */ private static boolean isModified(Context context, File archive, long currentCrc) { SharedPreferences prefs = getMultiDexPreferences(context); return (prefs.getLong(KEY_TIME_STAMP, NO_VALUE) != getTimeStamp(archive)) @@ -227,20 +272,27 @@ final class MultiDexExtractor { return files; } + /** + * Save {@link SharedPreferences}. Should be called only while owning the lock on + * {@link #LOCK_FILENAME}. + */ private static void putStoredApkInfo(Context context, long timeStamp, long crc, int totalDexNumber) { SharedPreferences prefs = getMultiDexPreferences(context); SharedPreferences.Editor edit = prefs.edit(); edit.putLong(KEY_TIME_STAMP, timeStamp); edit.putLong(KEY_CRC, crc); - /* SharedPreferences.Editor doc says that apply() and commit() "atomically performs the - * requested modifications" it should be OK to rely on saving the dex files number (getting - * old number value would go along with old crc and time stamp). - */ edit.putInt(KEY_DEX_NUMBER, totalDexNumber); - apply(edit); + /* Use commit() and not apply() as advised by the doc because we need synchronous writing of + * the editor content and apply is doing an "asynchronous commit to disk". + */ + edit.commit(); } + /** + * Get the MuliDex {@link SharedPreferences} for the current application. Should be called only + * while owning the lock on {@link #LOCK_FILENAME}. + */ private static SharedPreferences getMultiDexPreferences(Context context) { return context.getSharedPreferences(PREFS_FILE, Build.VERSION.SDK_INT < 11 /* Build.VERSION_CODES.HONEYCOMB */ @@ -249,15 +301,16 @@ final class MultiDexExtractor { } /** - * This removes any files that do not have the correct prefix. + * This removes old files. */ private static void prepareDexDir(File dexDir, final String extractedFilePrefix) { - // Clean possible old files FileFilter filter = new FileFilter() { @Override public boolean accept(File pathname) { - return !pathname.getName().startsWith(extractedFilePrefix); + String name = pathname.getName(); + return !(name.startsWith(extractedFilePrefix) + || name.equals(LOCK_FILENAME)); } }; File[] files = dexDir.listFiles(filter); @@ -343,30 +396,4 @@ final class MultiDexExtractor { Log.w(TAG, "Failed to close resource", e); } } - - // The following is taken from SharedPreferencesCompat to avoid having a dependency of the - // multidex support library on another support library. - private static Method sApplyMethod; // final - static { - try { - Class<?> cls = SharedPreferences.Editor.class; - sApplyMethod = cls.getMethod("apply"); - } catch (NoSuchMethodException unused) { - sApplyMethod = null; - } - } - - private static void apply(SharedPreferences.Editor editor) { - if (sApplyMethod != null) { - try { - sApplyMethod.invoke(editor); - return; - } catch (InvocationTargetException unused) { - // fall through - } catch (IllegalAccessException unused) { - // fall through - } - } - editor.commit(); - } } |