Initial import of ADIF API reverse-engineering toolkit
This commit is contained in:
@@ -0,0 +1,340 @@
|
||||
package com.google.firebase.crashlytics.internal.persistence;
|
||||
|
||||
import C.w;
|
||||
import L.b;
|
||||
import N2.a;
|
||||
import com.google.firebase.crashlytics.internal.Logger;
|
||||
import com.google.firebase.crashlytics.internal.common.CrashlyticsAppQualitySessionsSubscriber;
|
||||
import com.google.firebase.crashlytics.internal.common.CrashlyticsReportWithSessionId;
|
||||
import com.google.firebase.crashlytics.internal.metadata.UserMetadata;
|
||||
import com.google.firebase.crashlytics.internal.model.CrashlyticsReport;
|
||||
import com.google.firebase.crashlytics.internal.model.serialization.CrashlyticsReportJsonTransform;
|
||||
import com.google.firebase.crashlytics.internal.settings.SettingsProvider;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.FilenameFilter;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/* loaded from: classes3.dex */
|
||||
public class CrashlyticsReportPersistence {
|
||||
private static final String EVENT_COUNTER_FORMAT = "%010d";
|
||||
private static final int EVENT_COUNTER_WIDTH = 10;
|
||||
private static final String EVENT_FILE_NAME_PREFIX = "event";
|
||||
private static final int MAX_OPEN_SESSIONS = 8;
|
||||
private static final String NORMAL_EVENT_SUFFIX = "";
|
||||
private static final String PRIORITY_EVENT_SUFFIX = "_";
|
||||
private static final String REPORT_FILE_NAME = "report";
|
||||
private static final String SESSION_START_TIMESTAMP_FILE_NAME = "start-time";
|
||||
private final AtomicInteger eventCounter = new AtomicInteger(0);
|
||||
private final FileStore fileStore;
|
||||
private final CrashlyticsAppQualitySessionsSubscriber sessionsSubscriber;
|
||||
private final SettingsProvider settingsProvider;
|
||||
private static final Charset UTF_8 = Charset.forName("UTF-8");
|
||||
private static final int EVENT_NAME_LENGTH = 15;
|
||||
private static final CrashlyticsReportJsonTransform TRANSFORM = new CrashlyticsReportJsonTransform();
|
||||
private static final Comparator<? super File> LATEST_SESSION_ID_FIRST_COMPARATOR = new b(2);
|
||||
private static final FilenameFilter EVENT_FILE_FILTER = new a(0);
|
||||
|
||||
public CrashlyticsReportPersistence(FileStore fileStore, SettingsProvider settingsProvider, CrashlyticsAppQualitySessionsSubscriber crashlyticsAppQualitySessionsSubscriber) {
|
||||
this.fileStore = fileStore;
|
||||
this.settingsProvider = settingsProvider;
|
||||
this.sessionsSubscriber = crashlyticsAppQualitySessionsSubscriber;
|
||||
}
|
||||
|
||||
private SortedSet<String> capAndGetOpenSessions(String str) {
|
||||
this.fileStore.cleanupPreviousFileSystems();
|
||||
SortedSet<String> openSessionIds = getOpenSessionIds();
|
||||
if (str != null) {
|
||||
openSessionIds.remove(str);
|
||||
}
|
||||
if (openSessionIds.size() > 8) {
|
||||
while (openSessionIds.size() > 8) {
|
||||
String last = openSessionIds.last();
|
||||
Logger.getLogger().d("Removing session over cap: " + last);
|
||||
this.fileStore.deleteSessionFiles(last);
|
||||
openSessionIds.remove(last);
|
||||
}
|
||||
}
|
||||
return openSessionIds;
|
||||
}
|
||||
|
||||
private static int capFilesCount(List<File> list, int i) {
|
||||
int size = list.size();
|
||||
for (File file : list) {
|
||||
if (size <= i) {
|
||||
break;
|
||||
}
|
||||
FileStore.recursiveDelete(file);
|
||||
size--;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
private void capFinalizedReports() {
|
||||
int i = this.settingsProvider.getSettingsSync().sessionData.maxCompleteSessionsCount;
|
||||
List<File> allFinalizedReportFiles = getAllFinalizedReportFiles();
|
||||
int size = allFinalizedReportFiles.size();
|
||||
if (size <= i) {
|
||||
return;
|
||||
}
|
||||
Iterator<File> it = allFinalizedReportFiles.subList(i, size).iterator();
|
||||
while (it.hasNext()) {
|
||||
it.next().delete();
|
||||
}
|
||||
}
|
||||
|
||||
private static long convertTimestampFromSecondsToMs(long j4) {
|
||||
return j4 * 1000;
|
||||
}
|
||||
|
||||
private void deleteFiles(List<File> list) {
|
||||
Iterator<File> it = list.iterator();
|
||||
while (it.hasNext()) {
|
||||
it.next().delete();
|
||||
}
|
||||
}
|
||||
|
||||
private static String generateEventFilename(int i, boolean z3) {
|
||||
return w.o(EVENT_FILE_NAME_PREFIX, String.format(Locale.US, EVENT_COUNTER_FORMAT, Integer.valueOf(i)), z3 ? PRIORITY_EVENT_SUFFIX : "");
|
||||
}
|
||||
|
||||
private List<File> getAllFinalizedReportFiles() {
|
||||
ArrayList arrayList = new ArrayList();
|
||||
arrayList.addAll(this.fileStore.getPriorityReports());
|
||||
arrayList.addAll(this.fileStore.getNativeReports());
|
||||
Comparator<? super File> comparator = LATEST_SESSION_ID_FIRST_COMPARATOR;
|
||||
Collections.sort(arrayList, comparator);
|
||||
List<File> reports = this.fileStore.getReports();
|
||||
Collections.sort(reports, comparator);
|
||||
arrayList.addAll(reports);
|
||||
return arrayList;
|
||||
}
|
||||
|
||||
private static String getEventNameWithoutPriority(String str) {
|
||||
return str.substring(0, EVENT_NAME_LENGTH);
|
||||
}
|
||||
|
||||
private static boolean isHighPriorityEventFile(String str) {
|
||||
return str.startsWith(EVENT_FILE_NAME_PREFIX) && str.endsWith(PRIORITY_EVENT_SUFFIX);
|
||||
}
|
||||
|
||||
public static boolean isNormalPriorityEventFile(File file, String str) {
|
||||
return str.startsWith(EVENT_FILE_NAME_PREFIX) && !str.endsWith(PRIORITY_EVENT_SUFFIX);
|
||||
}
|
||||
|
||||
public static /* synthetic */ int lambda$static$0(File file, File file2) {
|
||||
return file2.getName().compareTo(file.getName());
|
||||
}
|
||||
|
||||
public static /* synthetic */ boolean lambda$static$1(File file, String str) {
|
||||
return str.startsWith(EVENT_FILE_NAME_PREFIX);
|
||||
}
|
||||
|
||||
public static int oldestEventFileFirst(File file, File file2) {
|
||||
return getEventNameWithoutPriority(file.getName()).compareTo(getEventNameWithoutPriority(file2.getName()));
|
||||
}
|
||||
|
||||
private static String readTextFile(File file) throws IOException {
|
||||
byte[] bArr = new byte[8192];
|
||||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
||||
FileInputStream fileInputStream = new FileInputStream(file);
|
||||
while (true) {
|
||||
try {
|
||||
int read = fileInputStream.read(bArr);
|
||||
if (read <= 0) {
|
||||
String str = new String(byteArrayOutputStream.toByteArray(), UTF_8);
|
||||
fileInputStream.close();
|
||||
return str;
|
||||
}
|
||||
byteArrayOutputStream.write(bArr, 0, read);
|
||||
} catch (Throwable th) {
|
||||
try {
|
||||
fileInputStream.close();
|
||||
} catch (Throwable th2) {
|
||||
th.addSuppressed(th2);
|
||||
}
|
||||
throw th;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void synthesizeNativeReportFile(File file, CrashlyticsReport.FilesPayload filesPayload, String str, CrashlyticsReport.ApplicationExitInfo applicationExitInfo) {
|
||||
String appQualitySessionId = this.sessionsSubscriber.getAppQualitySessionId(str);
|
||||
try {
|
||||
CrashlyticsReportJsonTransform crashlyticsReportJsonTransform = TRANSFORM;
|
||||
writeTextFile(this.fileStore.getNativeReport(str), crashlyticsReportJsonTransform.reportToJson(crashlyticsReportJsonTransform.reportFromJson(readTextFile(file)).withNdkPayload(filesPayload).withApplicationExitInfo(applicationExitInfo).withAppQualitySessionId(appQualitySessionId)));
|
||||
} catch (IOException e4) {
|
||||
Logger.getLogger().w("Could not synthesize final native report file for " + file, e4);
|
||||
}
|
||||
}
|
||||
|
||||
private void synthesizeReport(String str, long j4) {
|
||||
boolean z3;
|
||||
List<File> sessionFiles = this.fileStore.getSessionFiles(str, EVENT_FILE_FILTER);
|
||||
if (sessionFiles.isEmpty()) {
|
||||
Logger.getLogger().v("Session " + str + " has no events.");
|
||||
return;
|
||||
}
|
||||
Collections.sort(sessionFiles);
|
||||
ArrayList arrayList = new ArrayList();
|
||||
loop0: while (true) {
|
||||
z3 = false;
|
||||
for (File file : sessionFiles) {
|
||||
try {
|
||||
arrayList.add(TRANSFORM.eventFromJson(readTextFile(file)));
|
||||
} catch (IOException e4) {
|
||||
Logger.getLogger().w("Could not add event to report for " + file, e4);
|
||||
}
|
||||
if (z3 || isHighPriorityEventFile(file.getName())) {
|
||||
z3 = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!arrayList.isEmpty()) {
|
||||
synthesizeReportFile(this.fileStore.getSessionFile(str, REPORT_FILE_NAME), arrayList, j4, z3, UserMetadata.readUserId(str, this.fileStore), this.sessionsSubscriber.getAppQualitySessionId(str));
|
||||
} else {
|
||||
Logger.getLogger().w("Could not parse event files for session " + str);
|
||||
}
|
||||
}
|
||||
|
||||
private void synthesizeReportFile(File file, List<CrashlyticsReport.Session.Event> list, long j4, boolean z3, String str, String str2) {
|
||||
try {
|
||||
CrashlyticsReportJsonTransform crashlyticsReportJsonTransform = TRANSFORM;
|
||||
CrashlyticsReport withEvents = crashlyticsReportJsonTransform.reportFromJson(readTextFile(file)).withSessionEndFields(j4, z3, str).withAppQualitySessionId(str2).withEvents(list);
|
||||
CrashlyticsReport.Session session = withEvents.getSession();
|
||||
if (session == null) {
|
||||
return;
|
||||
}
|
||||
Logger.getLogger().d("appQualitySessionId: " + str2);
|
||||
writeTextFile(z3 ? this.fileStore.getPriorityReport(session.getIdentifier()) : this.fileStore.getReport(session.getIdentifier()), crashlyticsReportJsonTransform.reportToJson(withEvents));
|
||||
} catch (IOException e4) {
|
||||
Logger.getLogger().w("Could not synthesize final report file for " + file, e4);
|
||||
}
|
||||
}
|
||||
|
||||
private int trimEvents(String str, int i) {
|
||||
List<File> sessionFiles = this.fileStore.getSessionFiles(str, new a(1));
|
||||
Collections.sort(sessionFiles, new b(3));
|
||||
return capFilesCount(sessionFiles, i);
|
||||
}
|
||||
|
||||
private static void writeTextFile(File file, String str) throws IOException {
|
||||
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(new FileOutputStream(file), UTF_8);
|
||||
try {
|
||||
outputStreamWriter.write(str);
|
||||
outputStreamWriter.close();
|
||||
} catch (Throwable th) {
|
||||
try {
|
||||
outputStreamWriter.close();
|
||||
} catch (Throwable th2) {
|
||||
th.addSuppressed(th2);
|
||||
}
|
||||
throw th;
|
||||
}
|
||||
}
|
||||
|
||||
public void deleteAllReports() {
|
||||
deleteFiles(this.fileStore.getReports());
|
||||
deleteFiles(this.fileStore.getPriorityReports());
|
||||
deleteFiles(this.fileStore.getNativeReports());
|
||||
}
|
||||
|
||||
public void finalizeReports(String str, long j4) {
|
||||
for (String str2 : capAndGetOpenSessions(str)) {
|
||||
Logger.getLogger().v("Finalizing report for session " + str2);
|
||||
synthesizeReport(str2, j4);
|
||||
this.fileStore.deleteSessionFiles(str2);
|
||||
}
|
||||
capFinalizedReports();
|
||||
}
|
||||
|
||||
public void finalizeSessionWithNativeEvent(String str, CrashlyticsReport.FilesPayload filesPayload, CrashlyticsReport.ApplicationExitInfo applicationExitInfo) {
|
||||
File sessionFile = this.fileStore.getSessionFile(str, REPORT_FILE_NAME);
|
||||
Logger.getLogger().d("Writing native session report for " + str + " to file: " + sessionFile);
|
||||
synthesizeNativeReportFile(sessionFile, filesPayload, str, applicationExitInfo);
|
||||
}
|
||||
|
||||
public SortedSet<String> getOpenSessionIds() {
|
||||
return new TreeSet(this.fileStore.getAllOpenSessionIds()).descendingSet();
|
||||
}
|
||||
|
||||
public long getStartTimestampMillis(String str) {
|
||||
return this.fileStore.getSessionFile(str, SESSION_START_TIMESTAMP_FILE_NAME).lastModified();
|
||||
}
|
||||
|
||||
public boolean hasFinalizedReports() {
|
||||
return (this.fileStore.getReports().isEmpty() && this.fileStore.getPriorityReports().isEmpty() && this.fileStore.getNativeReports().isEmpty()) ? false : true;
|
||||
}
|
||||
|
||||
public List<CrashlyticsReportWithSessionId> loadFinalizedReports() {
|
||||
List<File> allFinalizedReportFiles = getAllFinalizedReportFiles();
|
||||
ArrayList arrayList = new ArrayList();
|
||||
for (File file : allFinalizedReportFiles) {
|
||||
try {
|
||||
arrayList.add(CrashlyticsReportWithSessionId.create(TRANSFORM.reportFromJson(readTextFile(file)), file.getName(), file));
|
||||
} catch (IOException e4) {
|
||||
Logger.getLogger().w("Could not load report file " + file + "; deleting", e4);
|
||||
file.delete();
|
||||
}
|
||||
}
|
||||
return arrayList;
|
||||
}
|
||||
|
||||
public void persistEvent(CrashlyticsReport.Session.Event event, String str) {
|
||||
persistEvent(event, str, false);
|
||||
}
|
||||
|
||||
public void persistReport(CrashlyticsReport crashlyticsReport) {
|
||||
CrashlyticsReport.Session session = crashlyticsReport.getSession();
|
||||
if (session == null) {
|
||||
Logger.getLogger().d("Could not get session for report");
|
||||
return;
|
||||
}
|
||||
String identifier = session.getIdentifier();
|
||||
try {
|
||||
writeTextFile(this.fileStore.getSessionFile(identifier, REPORT_FILE_NAME), TRANSFORM.reportToJson(crashlyticsReport));
|
||||
writeTextFile(this.fileStore.getSessionFile(identifier, SESSION_START_TIMESTAMP_FILE_NAME), "", session.getStartedAt());
|
||||
} catch (IOException e4) {
|
||||
Logger.getLogger().d("Could not persist report for session " + identifier, e4);
|
||||
}
|
||||
}
|
||||
|
||||
public void persistEvent(CrashlyticsReport.Session.Event event, String str, boolean z3) {
|
||||
int i = this.settingsProvider.getSettingsSync().sessionData.maxCustomExceptionEvents;
|
||||
try {
|
||||
writeTextFile(this.fileStore.getSessionFile(str, generateEventFilename(this.eventCounter.getAndIncrement(), z3)), TRANSFORM.eventToJson(event));
|
||||
} catch (IOException e4) {
|
||||
Logger.getLogger().w("Could not persist event for session " + str, e4);
|
||||
}
|
||||
trimEvents(str, i);
|
||||
}
|
||||
|
||||
private static void writeTextFile(File file, String str, long j4) throws IOException {
|
||||
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(new FileOutputStream(file), UTF_8);
|
||||
try {
|
||||
outputStreamWriter.write(str);
|
||||
file.setLastModified(convertTimestampFromSecondsToMs(j4));
|
||||
outputStreamWriter.close();
|
||||
} catch (Throwable th) {
|
||||
try {
|
||||
outputStreamWriter.close();
|
||||
} catch (Throwable th2) {
|
||||
th.addSuppressed(th2);
|
||||
}
|
||||
throw th;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
package com.google.firebase.crashlytics.internal.persistence;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import com.google.firebase.crashlytics.internal.Logger;
|
||||
import java.io.File;
|
||||
import java.io.FilenameFilter;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/* loaded from: classes3.dex */
|
||||
public class FileStore {
|
||||
private static final String CRASHLYTICS_PATH_V1 = ".com.google.firebase.crashlytics.files.v1";
|
||||
private static final String CRASHLYTICS_PATH_V2 = ".com.google.firebase.crashlytics.files.v2";
|
||||
private static final String NATIVE_REPORTS_PATH = "native-reports";
|
||||
private static final String NATIVE_SESSION_SUBDIR = "native";
|
||||
private static final String PRIORITY_REPORTS_PATH = "priority-reports";
|
||||
private static final String REPORTS_PATH = "reports";
|
||||
private static final String SESSIONS_PATH = "open-sessions";
|
||||
private final File crashlyticsDir;
|
||||
private final File filesDir;
|
||||
private final File nativeReportsDir;
|
||||
private final File priorityReportsDir;
|
||||
private final File reportsDir;
|
||||
private final File sessionsDir;
|
||||
|
||||
public FileStore(Context context) {
|
||||
String str;
|
||||
File filesDir = context.getFilesDir();
|
||||
this.filesDir = filesDir;
|
||||
if (useV2FileSystem()) {
|
||||
str = CRASHLYTICS_PATH_V2 + File.pathSeparator + sanitizeName(Application.getProcessName());
|
||||
} else {
|
||||
str = CRASHLYTICS_PATH_V1;
|
||||
}
|
||||
File prepareBaseDir = prepareBaseDir(new File(filesDir, str));
|
||||
this.crashlyticsDir = prepareBaseDir;
|
||||
this.sessionsDir = prepareBaseDir(new File(prepareBaseDir, SESSIONS_PATH));
|
||||
this.reportsDir = prepareBaseDir(new File(prepareBaseDir, REPORTS_PATH));
|
||||
this.priorityReportsDir = prepareBaseDir(new File(prepareBaseDir, PRIORITY_REPORTS_PATH));
|
||||
this.nativeReportsDir = prepareBaseDir(new File(prepareBaseDir, NATIVE_REPORTS_PATH));
|
||||
}
|
||||
|
||||
private void cleanupDir(File file) {
|
||||
if (file.exists() && recursiveDelete(file)) {
|
||||
Logger.getLogger().d("Deleted previous Crashlytics file system: " + file.getPath());
|
||||
}
|
||||
}
|
||||
|
||||
private File getSessionDir(String str) {
|
||||
return prepareDir(new File(this.sessionsDir, str));
|
||||
}
|
||||
|
||||
private static synchronized File prepareBaseDir(File file) {
|
||||
synchronized (FileStore.class) {
|
||||
try {
|
||||
if (file.exists()) {
|
||||
if (file.isDirectory()) {
|
||||
return file;
|
||||
}
|
||||
Logger.getLogger().d("Unexpected non-directory file: " + file + "; deleting file and creating new directory.");
|
||||
file.delete();
|
||||
}
|
||||
if (!file.mkdirs()) {
|
||||
Logger.getLogger().e("Could not create Crashlytics-specific directory: " + file);
|
||||
}
|
||||
return file;
|
||||
} catch (Throwable th) {
|
||||
throw th;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static File prepareDir(File file) {
|
||||
file.mkdirs();
|
||||
return file;
|
||||
}
|
||||
|
||||
public static boolean recursiveDelete(File file) {
|
||||
File[] listFiles = file.listFiles();
|
||||
if (listFiles != null) {
|
||||
for (File file2 : listFiles) {
|
||||
recursiveDelete(file2);
|
||||
}
|
||||
}
|
||||
return file.delete();
|
||||
}
|
||||
|
||||
private static <T> List<T> safeArrayToList(T[] tArr) {
|
||||
return tArr == null ? Collections.EMPTY_LIST : Arrays.asList(tArr);
|
||||
}
|
||||
|
||||
public static String sanitizeName(String str) {
|
||||
return str.replaceAll("[^a-zA-Z0-9.]", "_");
|
||||
}
|
||||
|
||||
@SuppressLint({"AnnotateVersionCheck"})
|
||||
private static boolean useV2FileSystem() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void cleanupPreviousFileSystems() {
|
||||
cleanupDir(new File(this.filesDir, ".com.google.firebase.crashlytics"));
|
||||
cleanupDir(new File(this.filesDir, ".com.google.firebase.crashlytics-ndk"));
|
||||
if (useV2FileSystem()) {
|
||||
cleanupDir(new File(this.filesDir, CRASHLYTICS_PATH_V1));
|
||||
}
|
||||
}
|
||||
|
||||
public void deleteAllCrashlyticsFiles() {
|
||||
recursiveDelete(this.crashlyticsDir);
|
||||
}
|
||||
|
||||
public boolean deleteSessionFiles(String str) {
|
||||
return recursiveDelete(new File(this.sessionsDir, str));
|
||||
}
|
||||
|
||||
public List<String> getAllOpenSessionIds() {
|
||||
return safeArrayToList(this.sessionsDir.list());
|
||||
}
|
||||
|
||||
public File getCommonFile(String str) {
|
||||
return new File(this.crashlyticsDir, str);
|
||||
}
|
||||
|
||||
public List<File> getCommonFiles(FilenameFilter filenameFilter) {
|
||||
return safeArrayToList(this.crashlyticsDir.listFiles(filenameFilter));
|
||||
}
|
||||
|
||||
public File getNativeReport(String str) {
|
||||
return new File(this.nativeReportsDir, str);
|
||||
}
|
||||
|
||||
public List<File> getNativeReports() {
|
||||
return safeArrayToList(this.nativeReportsDir.listFiles());
|
||||
}
|
||||
|
||||
public File getNativeSessionDir(String str) {
|
||||
return prepareDir(new File(getSessionDir(str), NATIVE_SESSION_SUBDIR));
|
||||
}
|
||||
|
||||
public File getPriorityReport(String str) {
|
||||
return new File(this.priorityReportsDir, str);
|
||||
}
|
||||
|
||||
public List<File> getPriorityReports() {
|
||||
return safeArrayToList(this.priorityReportsDir.listFiles());
|
||||
}
|
||||
|
||||
public File getReport(String str) {
|
||||
return new File(this.reportsDir, str);
|
||||
}
|
||||
|
||||
public List<File> getReports() {
|
||||
return safeArrayToList(this.reportsDir.listFiles());
|
||||
}
|
||||
|
||||
public File getSessionFile(String str, String str2) {
|
||||
return new File(getSessionDir(str), str2);
|
||||
}
|
||||
|
||||
public List<File> getSessionFiles(String str, FilenameFilter filenameFilter) {
|
||||
return safeArrayToList(getSessionDir(str).listFiles(filenameFilter));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user