300 lines
12 KiB
Java
300 lines
12 KiB
Java
package androidx.multidex;
|
|
|
|
import android.content.Context;
|
|
import android.content.SharedPreferences;
|
|
import android.os.Build;
|
|
import android.util.Log;
|
|
import java.io.BufferedOutputStream;
|
|
import java.io.Closeable;
|
|
import java.io.File;
|
|
import java.io.FileFilter;
|
|
import java.io.FileNotFoundException;
|
|
import java.io.FileOutputStream;
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
import java.io.RandomAccessFile;
|
|
import java.nio.channels.FileChannel;
|
|
import java.nio.channels.FileLock;
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
import java.util.zip.ZipEntry;
|
|
import java.util.zip.ZipFile;
|
|
import java.util.zip.ZipOutputStream;
|
|
|
|
/* loaded from: classes.dex */
|
|
final class MultiDexExtractor implements Closeable {
|
|
private final File a;
|
|
private final long b;
|
|
private final File c;
|
|
private final RandomAccessFile d;
|
|
private final FileChannel e;
|
|
private final FileLock f;
|
|
|
|
private static class ExtractedDex extends File {
|
|
public long a;
|
|
|
|
public ExtractedDex(File file, String str) {
|
|
super(file, str);
|
|
this.a = -1L;
|
|
}
|
|
}
|
|
|
|
MultiDexExtractor(File file, File file2) throws IOException {
|
|
Log.i("MultiDex", "MultiDexExtractor(" + file.getPath() + ", " + file2.getPath() + ")");
|
|
this.a = file;
|
|
this.c = file2;
|
|
this.b = b(file);
|
|
File file3 = new File(file2, "MultiDex.lock");
|
|
this.d = new RandomAccessFile(file3, "rw");
|
|
try {
|
|
this.e = this.d.getChannel();
|
|
try {
|
|
Log.i("MultiDex", "Blocking on lock " + file3.getPath());
|
|
this.f = this.e.lock();
|
|
Log.i("MultiDex", file3.getPath() + " locked");
|
|
} catch (IOException e) {
|
|
e = e;
|
|
a(this.e);
|
|
throw e;
|
|
} catch (Error e2) {
|
|
e = e2;
|
|
a(this.e);
|
|
throw e;
|
|
} catch (RuntimeException e3) {
|
|
e = e3;
|
|
a(this.e);
|
|
throw e;
|
|
}
|
|
} catch (IOException | Error | RuntimeException e4) {
|
|
a(this.d);
|
|
throw e4;
|
|
}
|
|
}
|
|
|
|
private static long b(File file) throws IOException {
|
|
long a = ZipUtil.a(file);
|
|
return a == -1 ? a - 1 : a;
|
|
}
|
|
|
|
List<? extends File> a(Context context, String str, boolean z) throws IOException {
|
|
List<ExtractedDex> b;
|
|
Log.i("MultiDex", "MultiDexExtractor.load(" + this.a.getPath() + ", " + z + ", " + str + ")");
|
|
if (!this.f.isValid()) {
|
|
throw new IllegalStateException("MultiDexExtractor was closed");
|
|
}
|
|
if (z || a(context, this.a, this.b, str)) {
|
|
if (z) {
|
|
Log.i("MultiDex", "Forced extraction must be performed.");
|
|
} else {
|
|
Log.i("MultiDex", "Detected that extraction must be performed.");
|
|
}
|
|
b = b();
|
|
a(context, str, a(this.a), this.b, b);
|
|
} else {
|
|
try {
|
|
b = a(context, str);
|
|
} catch (IOException e) {
|
|
Log.w("MultiDex", "Failed to reload existing extracted secondary dex files, falling back to fresh extraction", e);
|
|
b = b();
|
|
a(context, str, a(this.a), this.b, b);
|
|
}
|
|
}
|
|
Log.i("MultiDex", "load found " + b.size() + " secondary dex files");
|
|
return b;
|
|
}
|
|
|
|
@Override // java.io.Closeable, java.lang.AutoCloseable
|
|
public void close() throws IOException {
|
|
this.f.release();
|
|
this.e.close();
|
|
this.d.close();
|
|
}
|
|
|
|
private List<ExtractedDex> b() throws IOException {
|
|
boolean z;
|
|
String str = this.a.getName() + ".classes";
|
|
a();
|
|
ArrayList arrayList = new ArrayList();
|
|
ZipFile zipFile = new ZipFile(this.a);
|
|
try {
|
|
ZipEntry entry = zipFile.getEntry("classes2.dex");
|
|
int i = 2;
|
|
while (entry != null) {
|
|
ExtractedDex extractedDex = new ExtractedDex(this.c, str + i + ".zip");
|
|
arrayList.add(extractedDex);
|
|
Log.i("MultiDex", "Extraction is needed for file " + extractedDex);
|
|
int i2 = 0;
|
|
boolean z2 = false;
|
|
while (i2 < 3 && !z2) {
|
|
int i3 = i2 + 1;
|
|
a(zipFile, entry, extractedDex, str);
|
|
try {
|
|
extractedDex.a = b(extractedDex);
|
|
z = true;
|
|
} catch (IOException e) {
|
|
Log.w("MultiDex", "Failed to read crc from " + extractedDex.getAbsolutePath(), e);
|
|
z = false;
|
|
}
|
|
StringBuilder sb = new StringBuilder();
|
|
sb.append("Extraction ");
|
|
sb.append(z ? "succeeded" : "failed");
|
|
sb.append(" '");
|
|
sb.append(extractedDex.getAbsolutePath());
|
|
sb.append("': length ");
|
|
sb.append(extractedDex.length());
|
|
sb.append(" - crc: ");
|
|
sb.append(extractedDex.a);
|
|
Log.i("MultiDex", sb.toString());
|
|
if (!z) {
|
|
extractedDex.delete();
|
|
if (extractedDex.exists()) {
|
|
Log.w("MultiDex", "Failed to delete corrupted secondary dex '" + extractedDex.getPath() + "'");
|
|
}
|
|
}
|
|
z2 = z;
|
|
i2 = i3;
|
|
}
|
|
if (!z2) {
|
|
throw new IOException("Could not create zip file " + extractedDex.getAbsolutePath() + " for secondary dex (" + i + ")");
|
|
}
|
|
i++;
|
|
entry = zipFile.getEntry("classes" + i + ".dex");
|
|
}
|
|
try {
|
|
zipFile.close();
|
|
} catch (IOException e2) {
|
|
Log.w("MultiDex", "Failed to close resource", e2);
|
|
}
|
|
return arrayList;
|
|
} finally {
|
|
}
|
|
}
|
|
|
|
private List<ExtractedDex> a(Context context, String str) throws IOException {
|
|
Log.i("MultiDex", "loading existing secondary dex files");
|
|
String str2 = this.a.getName() + ".classes";
|
|
SharedPreferences a = a(context);
|
|
int i = a.getInt(str + "dex.number", 1);
|
|
ArrayList arrayList = new ArrayList(i + (-1));
|
|
int i2 = 2;
|
|
while (i2 <= i) {
|
|
ExtractedDex extractedDex = new ExtractedDex(this.c, str2 + i2 + ".zip");
|
|
if (extractedDex.isFile()) {
|
|
extractedDex.a = b(extractedDex);
|
|
long j = a.getLong(str + "dex.crc." + i2, -1L);
|
|
long j2 = a.getLong(str + "dex.time." + i2, -1L);
|
|
long lastModified = extractedDex.lastModified();
|
|
if (j2 == lastModified) {
|
|
String str3 = str2;
|
|
SharedPreferences sharedPreferences = a;
|
|
if (j == extractedDex.a) {
|
|
arrayList.add(extractedDex);
|
|
i2++;
|
|
a = sharedPreferences;
|
|
str2 = str3;
|
|
}
|
|
}
|
|
throw new IOException("Invalid extracted dex: " + extractedDex + " (key \"" + str + "\"), expected modification time: " + j2 + ", modification time: " + lastModified + ", expected crc: " + j + ", file crc: " + extractedDex.a);
|
|
}
|
|
throw new IOException("Missing extracted secondary dex file '" + extractedDex.getPath() + "'");
|
|
}
|
|
return arrayList;
|
|
}
|
|
|
|
private static boolean a(Context context, File file, long j, String str) {
|
|
SharedPreferences a = a(context);
|
|
if (a.getLong(str + "timestamp", -1L) == a(file)) {
|
|
if (a.getLong(str + "crc", -1L) == j) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private static long a(File file) {
|
|
long lastModified = file.lastModified();
|
|
return lastModified == -1 ? lastModified - 1 : lastModified;
|
|
}
|
|
|
|
private static void a(Context context, String str, long j, long j2, List<ExtractedDex> list) {
|
|
SharedPreferences.Editor edit = a(context).edit();
|
|
edit.putLong(str + "timestamp", j);
|
|
edit.putLong(str + "crc", j2);
|
|
edit.putInt(str + "dex.number", list.size() + 1);
|
|
int i = 2;
|
|
for (ExtractedDex extractedDex : list) {
|
|
edit.putLong(str + "dex.crc." + i, extractedDex.a);
|
|
edit.putLong(str + "dex.time." + i, extractedDex.lastModified());
|
|
i++;
|
|
}
|
|
edit.commit();
|
|
}
|
|
|
|
private static SharedPreferences a(Context context) {
|
|
return context.getSharedPreferences("multidex.version", Build.VERSION.SDK_INT < 11 ? 0 : 4);
|
|
}
|
|
|
|
private void a() {
|
|
File[] listFiles = this.c.listFiles(new FileFilter(this) { // from class: androidx.multidex.MultiDexExtractor.1
|
|
@Override // java.io.FileFilter
|
|
public boolean accept(File file) {
|
|
return !file.getName().equals("MultiDex.lock");
|
|
}
|
|
});
|
|
if (listFiles == null) {
|
|
Log.w("MultiDex", "Failed to list secondary dex dir content (" + this.c.getPath() + ").");
|
|
return;
|
|
}
|
|
for (File file : listFiles) {
|
|
Log.i("MultiDex", "Trying to delete old file " + file.getPath() + " of size " + file.length());
|
|
if (file.delete()) {
|
|
Log.i("MultiDex", "Deleted old file " + file.getPath());
|
|
} else {
|
|
Log.w("MultiDex", "Failed to delete old file " + file.getPath());
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void a(ZipFile zipFile, ZipEntry zipEntry, File file, String str) throws IOException, FileNotFoundException {
|
|
InputStream inputStream = zipFile.getInputStream(zipEntry);
|
|
File createTempFile = File.createTempFile("tmp-" + str, ".zip", file.getParentFile());
|
|
Log.i("MultiDex", "Extracting " + createTempFile.getPath());
|
|
try {
|
|
ZipOutputStream zipOutputStream = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(createTempFile)));
|
|
try {
|
|
ZipEntry zipEntry2 = new ZipEntry("classes.dex");
|
|
zipEntry2.setTime(zipEntry.getTime());
|
|
zipOutputStream.putNextEntry(zipEntry2);
|
|
byte[] bArr = new byte[16384];
|
|
for (int read = inputStream.read(bArr); read != -1; read = inputStream.read(bArr)) {
|
|
zipOutputStream.write(bArr, 0, read);
|
|
}
|
|
zipOutputStream.closeEntry();
|
|
zipOutputStream.close();
|
|
if (createTempFile.setReadOnly()) {
|
|
Log.i("MultiDex", "Renaming to " + file.getPath());
|
|
if (createTempFile.renameTo(file)) {
|
|
return;
|
|
}
|
|
throw new IOException("Failed to rename \"" + createTempFile.getAbsolutePath() + "\" to \"" + file.getAbsolutePath() + "\"");
|
|
}
|
|
throw new IOException("Failed to mark readonly \"" + createTempFile.getAbsolutePath() + "\" (tmp of \"" + file.getAbsolutePath() + "\")");
|
|
} catch (Throwable th) {
|
|
zipOutputStream.close();
|
|
throw th;
|
|
}
|
|
} finally {
|
|
a(inputStream);
|
|
createTempFile.delete();
|
|
}
|
|
}
|
|
|
|
private static void a(Closeable closeable) {
|
|
try {
|
|
closeable.close();
|
|
} catch (IOException e) {
|
|
Log.w("MultiDex", "Failed to close resource", e);
|
|
}
|
|
}
|
|
}
|