blob: b80fcc822b76d1bc3e4c7b87141101a32e1c463b [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 multidex002.fakelibrary;
6
7import java.io.Closeable;
8import java.io.File;
9import java.io.FileFilter;
10import java.io.IOException;
11import java.util.List;
12import multidexfakeframeworks.Context;
13
14/**
15 * Exposes application secondary dex files as files in the application data
16 * directory.
17 */
18final 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
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
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
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
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 */
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 */
134 private static void closeQuietly(Closeable closeable) {
135 try {
136 closeable.close();
137 } catch (IOException e) {
138 }
139 }
140
141}