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