Remove the check for source apk file size
The file deletion when finding that the source apk file size
was different than what was stored in SharedPreferences causes a race
condition when multiple processes are executing this code, where one
process could erase the secondary zip file while a different process had
already started to create the zip file. This check is unnecessary and
has been removed.
Also, changed the parameter for getting the SharedPreferences to
Context.MODE_MULTI_PROCESS, which forces a re-read of the key/value
pairs from the file.
This should also fix the original bug b/12550057.
Bug: 12594955
Bug: 12550057
Change-Id: I53f4bca0d03a5737d802509274520a78effcf228
diff --git a/library/src/android/support/multidex/MultiDexExtractor.java b/library/src/android/support/multidex/MultiDexExtractor.java
index dfbe8f1..ce779d9 100644
--- a/library/src/android/support/multidex/MultiDexExtractor.java
+++ b/library/src/android/support/multidex/MultiDexExtractor.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
+import android.os.Build;
import android.util.Log;
import java.io.BufferedOutputStream;
@@ -60,7 +61,6 @@
private static final int BUFFER_SIZE = 0x4000;
private static final String PREFS_FILE = "multidex.version";
- private static final String KEY_APK_SIZE = "apk_size";
private static final String KEY_NUM_DEX_FILES = "num_dex";
private static final String KEY_PREFIX_DEX_CRC = "crc";
@@ -77,11 +77,13 @@
boolean forceReload) throws IOException {
Log.i(TAG, "load(" + applicationInfo.sourceDir + ", forceReload=" + forceReload + ")");
final File sourceApk = new File(applicationInfo.sourceDir);
- final long currentApkSize = sourceApk.length();
- final boolean isNewApk = getStoredApkSize(context) != currentApkSize;
final String extractedFilePrefix = sourceApk.getName() + EXTRACTED_NAME_EXT;
- prepareDexDir(dexDir, extractedFilePrefix, isNewApk);
+ // Ensure that whatever deletions happen in prepareDexDir only happen if the zip that
+ // contains a secondary dex file in there is not consistent with the latest apk. Otherwise,
+ // multi-process race conditions can cause a crash loop where one process deletes the zip
+ // while another had created it.
+ prepareDexDir(dexDir, extractedFilePrefix);
final List<File> files = new ArrayList<File>();
final ZipFile apk = new ZipFile(applicationInfo.sourceDir);
@@ -127,8 +129,7 @@
}
}
if (isExtractionSuccessful) {
- // Write the current apk size and the dex crc's into the shared preferences
- putStoredApkSize(context, currentApkSize);
+ // Write the dex crc's into the shared preferences
putStoredDexCrcs(context, dexCrcs);
} else {
throw new IOException("Could not create zip file " +
@@ -199,20 +200,8 @@
return false;
}
- private static long getStoredApkSize(Context context) {
- SharedPreferences prefs = context.getSharedPreferences(PREFS_FILE, 0);
- return prefs.getLong(KEY_APK_SIZE, -1L);
- }
-
- private static void putStoredApkSize(Context context, long apkSize) {
- SharedPreferences prefs = context.getSharedPreferences(PREFS_FILE, 0);
- SharedPreferences.Editor edit = prefs.edit();
- edit.putLong(KEY_APK_SIZE, apkSize);
- apply(edit);
- }
-
private static ArrayList<Long> getStoredDexCrcs(Context context) {
- SharedPreferences prefs = context.getSharedPreferences(PREFS_FILE, 0);
+ SharedPreferences prefs = getMultiDexPreferences(context);
int numDexFiles = prefs.getInt(KEY_NUM_DEX_FILES, 0);
ArrayList<Long> dexCrcs = new ArrayList<Long>(numDexFiles);
for (int i = 0; i < numDexFiles; i++) {
@@ -222,7 +211,7 @@
}
private static void putStoredDexCrcs(Context context, ArrayList<Long> dexCrcs) {
- SharedPreferences prefs = context.getSharedPreferences(PREFS_FILE, 0);
+ SharedPreferences prefs = getMultiDexPreferences(context);
SharedPreferences.Editor edit = prefs.edit();
edit.putInt(KEY_NUM_DEX_FILES, dexCrcs.size());
for (int i = 0; i < dexCrcs.size(); i++) {
@@ -231,16 +220,22 @@
apply(edit);
}
+ private static SharedPreferences getMultiDexPreferences(Context context) {
+ return context.getSharedPreferences(PREFS_FILE,
+ Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB
+ ? Context.MODE_PRIVATE
+ : Context.MODE_PRIVATE | Context.MODE_MULTI_PROCESS);
+ }
+
private static String makeDexCrcKey(int i) {
return KEY_PREFIX_DEX_CRC + Integer.toString(i);
}
/**
- * This always removes extraneous files. If {@code removeAll} is true, then any existing
- * zip and dex files are removed from the secondary-dexes directory.
+ * This removes any files that do not have the correct prefix.
*/
- private static void prepareDexDir(File dexDir, final String extractedFilePrefix,
- final boolean removeAll) throws IOException {
+ private static void prepareDexDir(File dexDir, final String extractedFilePrefix)
+ throws IOException {
dexDir.mkdir();
if (!dexDir.isDirectory()) {
throw new IOException("Failed to create dex directory " + dexDir.getPath());
@@ -251,7 +246,7 @@
@Override
public boolean accept(File pathname) {
- return removeAll || !pathname.getName().startsWith(extractedFilePrefix);
+ return !pathname.getName().startsWith(extractedFilePrefix);
}
};
File[] files = dexDir.listFiles(filter);