Use Context.getFilesDir as a backup dex location

On some devices it seems impossible to read or write the application
data directory. There, creating code_cache at the proper location is
impossible. In this case fallback to the 'files' directory. This may
lead to not cleaning the useless extracted secondary dex files if one
such devices is ever updated to L.

Bug: https://code.google.com/p/android/issues/detail?id=79388
Change-Id: I4b6725572f10fd511992dc8a5043d2f135abd3a5
diff --git a/library/src/android/support/multidex/MultiDex.java b/library/src/android/support/multidex/MultiDex.java
index 8d5e1ab..1e04c19 100644
--- a/library/src/android/support/multidex/MultiDex.java
+++ b/library/src/android/support/multidex/MultiDex.java
@@ -60,8 +60,9 @@
 
     private static final String OLD_SECONDARY_FOLDER_NAME = "secondary-dexes";
 
-    private static final String SECONDARY_FOLDER_NAME = "code_cache" + File.separator +
-        "secondary-dexes";
+    private static final String CODE_CACHE_NAME = "code_cache";
+
+    private static final String CODE_CACHE_SECONDARY_FOLDER_NAME = "secondary-dexes";
 
     private static final int MAX_SUPPORTED_SDK_VERSION = 20;
 
@@ -155,7 +156,7 @@
                       + "continuing without cleaning.", t);
                 }
 
-                File dexDir = new File(applicationInfo.dataDir, SECONDARY_FOLDER_NAME);
+                File dexDir = getDexDir(context, applicationInfo);
                 List<File> files = MultiDexExtractor.load(context, applicationInfo, dexDir, false);
                 if (checkValidZipFiles(files)) {
                     installSecondaryDexes(loader, dexDir, files);
@@ -363,6 +364,42 @@
         }
     }
 
+    private static File getDexDir(Context context, ApplicationInfo applicationInfo)
+            throws IOException {
+        File cache = new File(applicationInfo.dataDir, CODE_CACHE_NAME);
+        try {
+            mkdirChecked(cache);
+        } catch (IOException e) {
+            /* If we can't emulate code_cache, then store to filesDir. This means abandoning useless
+             * files on disk if the device ever updates to android 5+. But since this seems to
+             * happen only on some devices running android 2, this should cause no pollution.
+             */
+            cache = new File(context.getFilesDir(), CODE_CACHE_NAME);
+            mkdirChecked(cache);
+        }
+        File dexDir = new File(cache, CODE_CACHE_SECONDARY_FOLDER_NAME);
+        mkdirChecked(dexDir);
+        return dexDir;
+    }
+
+    private static void mkdirChecked(File dir) throws IOException {
+        dir.mkdir();
+        if (!dir.isDirectory()) {
+            File parent = dir.getParentFile();
+            if (parent == null) {
+                Log.e(TAG, "Failed to create dir " + dir.getPath() + ". Parent file is null.");
+            } else {
+                Log.e(TAG, "Failed to create dir " + dir.getPath() +
+                        ". parent file is a dir " + parent.isDirectory() +
+                        ", a file " + parent.isFile() +
+                        ", exists " + parent.exists() +
+                        ", readable " + parent.canRead() +
+                        ", writable " + parent.canWrite());
+            }
+            throw new IOException("Failed to create directory " + dir.getPath());
+        }
+    }
+
     /**
      * Installer for platform versions 19.
      */
diff --git a/library/src/android/support/multidex/MultiDexExtractor.java b/library/src/android/support/multidex/MultiDexExtractor.java
index 27da6b3..4aef9f2 100644
--- a/library/src/android/support/multidex/MultiDexExtractor.java
+++ b/library/src/android/support/multidex/MultiDexExtractor.java
@@ -251,15 +251,7 @@
     /**
      * This removes any files that do not have the correct prefix.
      */
-    private static void prepareDexDir(File dexDir, final String extractedFilePrefix)
-            throws IOException {
-        /* mkdirs() has some bugs, especially before jb-mr1 and we have only a maximum of one parent
-         * to create, lets stick to mkdir().
-         */
-        File cache = dexDir.getParentFile();
-        mkdirChecked(cache);
-        mkdirChecked(dexDir);
-
+    private static void prepareDexDir(File dexDir, final String extractedFilePrefix) {
         // Clean possible old files
         FileFilter filter = new FileFilter() {
 
@@ -284,24 +276,6 @@
         }
     }
 
-    private static void mkdirChecked(File dir) throws IOException {
-        dir.mkdir();
-        if (!dir.isDirectory()) {
-            File parent = dir.getParentFile();
-            if (parent == null) {
-                Log.e(TAG, "Failed to create dir " + dir.getPath() + ". Parent file is null.");
-            } else {
-                Log.e(TAG, "Failed to create dir " + dir.getPath() +
-                        ". parent file is a dir " + parent.isDirectory() +
-                        ", a file " + parent.isFile() +
-                        ", exists " + parent.exists() +
-                        ", readable " + parent.canRead() +
-                        ", writable " + parent.canWrite());
-            }
-            throw new IOException("Failed to create cache directory " + dir.getPath());
-        }
-    }
-
     private static void extract(ZipFile apk, ZipEntry dexFile, File extractTo,
             String extractedFilePrefix) throws IOException, FileNotFoundException {