• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4 
5 package multidex003.fakelibrary;
6 
7 import java.io.Closeable;
8 import java.io.File;
9 import java.io.FileFilter;
10 import java.io.IOException;
11 import java.util.List;
12 import multidexfakeframeworks.Context;
13 
14 /**
15  * Exposes application secondary dex files as files in the application data
16  * directory.
17  */
18 final class MultiDexExtractor {
19 
20     private static final String TAG = MultiDex.TAG;
21 
22     /**
23      * We look for additional dex files named {@code classes2.dex},
24      * {@code classes3.dex}, etc.
25      */
26     private static final String DEX_PREFIX = "classes";
27     private static final String DEX_SUFFIX = ".dex";
28 
29     private static final String EXTRACTED_NAME_EXT = ".classes";
30     private static final String EXTRACTED_SUFFIX = ".zip";
31     private static final int MAX_EXTRACT_ATTEMPTS = 3;
32 
33     private static final String PREFS_FILE = "multidex.version";
34     private static final String KEY_TIME_STAMP = "timestamp";
35     private static final String KEY_CRC = "crc";
36     private static final String KEY_DEX_NUMBER = "dex.number";
37 
38     /**
39      * Size of reading buffers.
40      */
41     private static final int BUFFER_SIZE = 0x4000;
42     /* Keep value away from 0 because it is a too probable time stamp value */
43     private static final long NO_VALUE = -1L;
44 
45 
loadExistingExtractions(Context context, File sourceApk, File dexDir)46     private static List<File> loadExistingExtractions(Context context, File sourceApk, File dexDir)
47             throws IOException {
48 
49         final String extractedFilePrefix = sourceApk.getName() + EXTRACTED_NAME_EXT;
50         int totalDexNumber = 1;
51         final List<File> files = null;
52 
53         for (int secondaryNumber = 2; secondaryNumber <= totalDexNumber; secondaryNumber++) {
54             String fileName = extractedFilePrefix + secondaryNumber + EXTRACTED_SUFFIX;
55             File extractedFile = new File(dexDir, fileName);
56             if (extractedFile.isFile()) {
57                 files.add(extractedFile);
58             } else {
59                 throw new IOException("Missing extracted secondary dex file '" +
60                         extractedFile.getPath() + "'");
61             }
62         }
63 
64         return files;
65     }
66 
getTimeStamp(File archive)67     private static long getTimeStamp(File archive) {
68         long timeStamp = archive.lastModified();
69         if (timeStamp == NO_VALUE) {
70             // never return NO_VALUE
71             timeStamp--;
72         }
73         return timeStamp;
74     }
75 
76 
getZipCrc(File archive)77     private static long getZipCrc(File archive) throws IOException {
78         long computedValue = ZipUtil.getZipCrc(archive);
79         if (computedValue == NO_VALUE) {
80             // never return NO_VALUE
81             computedValue--;
82         }
83         return computedValue;
84     }
85 
performExtractions(File sourceApk, File dexDir)86     private static List<File> performExtractions(File sourceApk, File dexDir)
87             throws IOException {
88 
89         final String extractedFilePrefix = sourceApk.getName() + EXTRACTED_NAME_EXT;
90 
91         // Ensure that whatever deletions happen in prepareDexDir only happen if the zip that
92         // contains a secondary dex file in there is not consistent with the latest apk.  Otherwise,
93         // multi-process race conditions can cause a crash loop where one process deletes the zip
94         // while another had created it.
95         prepareDexDir(dexDir, extractedFilePrefix);
96 
97         List<File> files = null;
98 
99         return files;
100     }
101 
102     /**
103      * This removes any files that do not have the correct prefix.
104      */
prepareDexDir(File dexDir, final String extractedFilePrefix)105     private static void prepareDexDir(File dexDir, final String extractedFilePrefix)
106             throws IOException {
107         dexDir.mkdir();
108         if (!dexDir.isDirectory()) {
109             throw new IOException("Failed to create dex directory " + dexDir.getPath());
110         }
111 
112         // Clean possible old files
113         FileFilter filter = new FileFilter() {
114 
115             @Override
116             public boolean accept(File pathname) {
117                 return !pathname.getName().startsWith(extractedFilePrefix);
118             }
119         };
120         File[] files = dexDir.listFiles(filter);
121         if (files == null) {
122             return;
123         }
124         for (File oldFile : files) {
125             if (!oldFile.delete()) {
126             } else {
127             }
128         }
129     }
130 
131     /**
132      * Closes the given {@code Closeable}. Suppresses any IO exceptions.
133      */
closeQuietly(Closeable closeable)134     private static void closeQuietly(Closeable closeable) {
135         try {
136             closeable.close();
137         } catch (IOException e) {
138         }
139     }
140 
141 }
142