mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-13 19:29:06 +01:00
Compare commits
6 Commits
2224-fix-b
...
1387-persi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5b9955d71f | ||
|
|
719e3c6138 | ||
|
|
1d04bbcb4f | ||
|
|
43b0d1d543 | ||
|
|
3f7c9af3a9 | ||
|
|
e79abeff2e |
@@ -5,12 +5,15 @@ import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.preference.PreferenceManager;
|
||||
|
||||
import org.briarproject.bramble.api.FeatureFlags;
|
||||
import org.briarproject.bramble.api.account.AccountManager;
|
||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
import org.briarproject.bramble.api.db.DatabaseConfig;
|
||||
import org.briarproject.bramble.api.identity.IdentityManager;
|
||||
import org.briarproject.bramble.api.logging.PersistentLogManager;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
@@ -23,14 +26,18 @@ import javax.inject.Inject;
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
import static java.util.Arrays.asList;
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
import static org.briarproject.bramble.util.AndroidUtils.getPersistentLogDir;
|
||||
import static org.briarproject.bramble.util.IoUtils.deleteFileOrDir;
|
||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
import static org.briarproject.bramble.util.LogUtils.logFileOrDir;
|
||||
|
||||
class AndroidAccountManager extends AccountManagerImpl
|
||||
implements AccountManager {
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(AndroidAccountManager.class.getName());
|
||||
getLogger(AndroidAccountManager.class.getName());
|
||||
|
||||
/**
|
||||
* Directories that shouldn't be deleted when deleting the user's account.
|
||||
@@ -40,13 +47,22 @@ class AndroidAccountManager extends AccountManagerImpl
|
||||
|
||||
protected final Context appContext;
|
||||
private final SharedPreferences prefs;
|
||||
private final PersistentLogManager logManager;
|
||||
private final FeatureFlags featureFlags;
|
||||
|
||||
@Inject
|
||||
AndroidAccountManager(DatabaseConfig databaseConfig,
|
||||
CryptoComponent crypto, IdentityManager identityManager,
|
||||
SharedPreferences prefs, Application app) {
|
||||
AndroidAccountManager(
|
||||
DatabaseConfig databaseConfig,
|
||||
CryptoComponent crypto,
|
||||
IdentityManager identityManager,
|
||||
SharedPreferences prefs,
|
||||
PersistentLogManager logManager,
|
||||
FeatureFlags featureFlags,
|
||||
Application app) {
|
||||
super(databaseConfig, crypto, identityManager);
|
||||
this.prefs = prefs;
|
||||
this.logManager = logManager;
|
||||
this.featureFlags = featureFlags;
|
||||
appContext = app.getApplicationContext();
|
||||
}
|
||||
|
||||
@@ -74,6 +90,9 @@ class AndroidAccountManager extends AccountManagerImpl
|
||||
LOG.info("Contents of account directory after deleting:");
|
||||
logFileOrDir(LOG, INFO, getDataDir());
|
||||
}
|
||||
if (featureFlags.shouldEnablePersistentLogs()) {
|
||||
replacePersistentLogger();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,4 +153,13 @@ class AndroidAccountManager extends AccountManagerImpl
|
||||
private void addIfNotNull(Set<File> files, @Nullable File file) {
|
||||
if (file != null) files.add(file);
|
||||
}
|
||||
|
||||
private void replacePersistentLogger() {
|
||||
File logDir = getPersistentLogDir(appContext);
|
||||
try {
|
||||
logManager.addLogHandler(logDir, getLogger(""));
|
||||
} catch (IOException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,10 +111,14 @@ public class AndroidUtils {
|
||||
return ctx.getDir(STORED_REPORTS, MODE_PRIVATE);
|
||||
}
|
||||
|
||||
public static File getLogcatFile(Context ctx) {
|
||||
public static File getTemporaryLogFile(Context ctx) {
|
||||
return new File(ctx.getFilesDir(), STORED_LOGCAT);
|
||||
}
|
||||
|
||||
public static File getPersistentLogDir(Context ctx) {
|
||||
return ctx.getDir("log", MODE_PRIVATE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of supported content types for image attachments.
|
||||
*/
|
||||
|
||||
@@ -4,9 +4,11 @@ import android.app.Application;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
|
||||
import org.briarproject.bramble.api.FeatureFlags;
|
||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
import org.briarproject.bramble.api.db.DatabaseConfig;
|
||||
import org.briarproject.bramble.api.identity.IdentityManager;
|
||||
import org.briarproject.bramble.api.logging.PersistentLogManager;
|
||||
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||
import org.jmock.Expectations;
|
||||
import org.jmock.lib.legacy.ClassImposteriser;
|
||||
@@ -15,7 +17,9 @@ import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import static android.content.Context.MODE_PRIVATE;
|
||||
import static junit.framework.Assert.assertFalse;
|
||||
import static junit.framework.Assert.assertTrue;
|
||||
import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory;
|
||||
@@ -27,6 +31,10 @@ public class AndroidAccountManagerTest extends BrambleMockTestCase {
|
||||
context.mock(SharedPreferences.class, "prefs");
|
||||
private final SharedPreferences defaultPrefs =
|
||||
context.mock(SharedPreferences.class, "defaultPrefs");
|
||||
private final PersistentLogManager logManager =
|
||||
context.mock(PersistentLogManager.class);
|
||||
private final FeatureFlags featureFlags =
|
||||
context.mock(FeatureFlags.class);
|
||||
private final DatabaseConfig databaseConfig =
|
||||
context.mock(DatabaseConfig.class);
|
||||
private final CryptoComponent crypto = context.mock(CryptoComponent.class);
|
||||
@@ -40,6 +48,7 @@ public class AndroidAccountManagerTest extends BrambleMockTestCase {
|
||||
private final File testDir = getTestDirectory();
|
||||
private final File keyDir = new File(testDir, "key");
|
||||
private final File dbDir = new File(testDir, "db");
|
||||
private final File logDir = new File(testDir, "log");
|
||||
|
||||
private AndroidAccountManager accountManager;
|
||||
|
||||
@@ -61,7 +70,7 @@ public class AndroidAccountManagerTest extends BrambleMockTestCase {
|
||||
will(returnValue(app));
|
||||
}});
|
||||
accountManager = new AndroidAccountManager(databaseConfig, crypto,
|
||||
identityManager, prefs, app) {
|
||||
identityManager, prefs, logManager, featureFlags, app) {
|
||||
@Override
|
||||
SharedPreferences getDefaultSharedPreferences() {
|
||||
return defaultPrefs;
|
||||
@@ -109,10 +118,17 @@ public class AndroidAccountManagerTest extends BrambleMockTestCase {
|
||||
will(returnValue(cacheDir));
|
||||
oneOf(app).getExternalCacheDir();
|
||||
will(returnValue(externalCacheDir));
|
||||
oneOf(featureFlags).shouldEnablePersistentLogs();
|
||||
will(returnValue(true));
|
||||
oneOf(app).getDir("log", MODE_PRIVATE);
|
||||
will(returnValue(logDir));
|
||||
oneOf(logManager).addLogHandler(with(logDir),
|
||||
with(any(Logger.class)));
|
||||
}});
|
||||
|
||||
assertTrue(dbDir.mkdirs());
|
||||
assertTrue(keyDir.mkdirs());
|
||||
assertTrue(logDir.mkdirs());
|
||||
assertTrue(codeCacheDir.mkdirs());
|
||||
assertTrue(codeCacheFile.createNewFile());
|
||||
assertTrue(libDir.mkdirs());
|
||||
@@ -130,6 +146,7 @@ public class AndroidAccountManagerTest extends BrambleMockTestCase {
|
||||
|
||||
assertFalse(dbDir.exists());
|
||||
assertFalse(keyDir.exists());
|
||||
assertFalse(logDir.exists());
|
||||
assertTrue(codeCacheDir.exists());
|
||||
assertTrue(codeCacheFile.exists());
|
||||
assertTrue(libDir.exists());
|
||||
|
||||
@@ -10,4 +10,6 @@ public interface FeatureFlags {
|
||||
boolean shouldEnableProfilePictures();
|
||||
|
||||
boolean shouldEnableDisappearingMessages();
|
||||
|
||||
boolean shouldEnablePersistentLogs();
|
||||
}
|
||||
|
||||
@@ -1,8 +1,16 @@
|
||||
package org.briarproject.bramble.api;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
|
||||
import java.util.Hashtable;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static org.briarproject.bramble.util.StringUtils.fromHexString;
|
||||
import static org.briarproject.bramble.util.StringUtils.toHexString;
|
||||
|
||||
@NotNullByDefault
|
||||
public abstract class StringMap extends Hashtable<String, String> {
|
||||
|
||||
protected StringMap(Map<String, String> m) {
|
||||
@@ -52,4 +60,19 @@ public abstract class StringMap extends Hashtable<String, String> {
|
||||
public void putLong(String key, long value) {
|
||||
put(key, String.valueOf(value));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public byte[] getBytes(String key) {
|
||||
String s = get(key);
|
||||
if (s == null) return null;
|
||||
try {
|
||||
return fromHexString(s);
|
||||
} catch (IllegalArgumentException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void putBytes(String key, byte[] value) {
|
||||
put(key, toHexString(value));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
package org.briarproject.bramble.api.logging;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.settings.Settings;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Scanner;
|
||||
import java.util.logging.Handler;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@NotNullByDefault
|
||||
public interface PersistentLogManager {
|
||||
|
||||
/**
|
||||
* The namespace of the (@link Settings) where the log key is stored.
|
||||
*/
|
||||
String LOG_SETTINGS_NAMESPACE = "log";
|
||||
|
||||
/**
|
||||
* The {@link Settings} key under which the log key is stored.
|
||||
*/
|
||||
String LOG_KEY_KEY = "logKey";
|
||||
|
||||
/**
|
||||
* Creates and returns a persistent log handler that stores its logs in
|
||||
* the given directory.
|
||||
*/
|
||||
Handler createLogHandler(File dir) throws IOException;
|
||||
|
||||
/**
|
||||
* Creates a persistent log handler that stores its logs in the given
|
||||
* directory and adds the handler to the given logger, replacing any
|
||||
* existing persistent log handler.
|
||||
*/
|
||||
void addLogHandler(File dir, Logger logger) throws IOException;
|
||||
|
||||
/**
|
||||
* Returns a {@link Scanner} for reading the persistent log entries stored
|
||||
* in the given directory.
|
||||
*
|
||||
* @param old True if the previous session's log should be loaded, or false
|
||||
* if the current session's log should be loaded
|
||||
*/
|
||||
Scanner getPersistentLog(File dir, boolean old) throws IOException;
|
||||
}
|
||||
@@ -8,11 +8,24 @@ import java.io.File;
|
||||
@NotNullByDefault
|
||||
public interface DevConfig {
|
||||
|
||||
/**
|
||||
* Returns the public key for encrypting feedback and crash reports.
|
||||
*/
|
||||
PublicKey getDevPublicKey();
|
||||
|
||||
/**
|
||||
* Returns the onion address for submitting feedback and crash reports.
|
||||
*/
|
||||
String getDevOnionAddress();
|
||||
|
||||
/**
|
||||
* Returns the directory for storing unsent feedback and crash reports.
|
||||
*/
|
||||
File getReportDir();
|
||||
|
||||
File getLogcatFile();
|
||||
/**
|
||||
* Returns the temporary file for passing the encrypted app log from the
|
||||
* main process to the crash reporter process.
|
||||
*/
|
||||
File getTemporaryLogFile();
|
||||
}
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
package org.briarproject.bramble.util;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Collection;
|
||||
import java.util.logging.Formatter;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.LogRecord;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import static java.util.logging.Level.FINE;
|
||||
@@ -57,4 +60,13 @@ public class LogUtils {
|
||||
String type) {
|
||||
logger.log(level, type + " " + f.getAbsolutePath() + " " + f.length());
|
||||
}
|
||||
|
||||
public static String formatLog(Formatter formatter,
|
||||
Collection<LogRecord> logRecords) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (LogRecord record : logRecords) {
|
||||
sb.append(formatter.format(record)).append('\n');
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,10 +51,3 @@ task jarTest(type: Jar, dependsOn: testClasses) {
|
||||
artifacts {
|
||||
testOutput jarTest
|
||||
}
|
||||
|
||||
test {
|
||||
// for random = context.mock(SecureRandom.class); in PollerImplTest
|
||||
jvmArgs = [
|
||||
'--add-opens', 'java.base/java.security=ALL-UNNAMED',
|
||||
]
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import org.briarproject.bramble.identity.IdentityModule;
|
||||
import org.briarproject.bramble.io.IoModule;
|
||||
import org.briarproject.bramble.keyagreement.KeyAgreementModule;
|
||||
import org.briarproject.bramble.lifecycle.LifecycleModule;
|
||||
import org.briarproject.bramble.logging.LoggingModule;
|
||||
import org.briarproject.bramble.mailbox.MailboxModule;
|
||||
import org.briarproject.bramble.plugin.PluginModule;
|
||||
import org.briarproject.bramble.properties.PropertiesModule;
|
||||
@@ -44,6 +45,7 @@ import dagger.Module;
|
||||
IoModule.class,
|
||||
KeyAgreementModule.class,
|
||||
LifecycleModule.class,
|
||||
LoggingModule.class,
|
||||
MailboxModule.class,
|
||||
PluginModule.class,
|
||||
PropertiesModule.class,
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
package org.briarproject.briar.android.logging;
|
||||
package org.briarproject.bramble.logging;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.TimeZone;
|
||||
import java.util.logging.Formatter;
|
||||
@@ -18,16 +17,6 @@ import static java.util.Locale.US;
|
||||
@NotNullByDefault
|
||||
public class BriefLogFormatter extends Formatter {
|
||||
|
||||
public static String formatLog(Formatter formatter,
|
||||
Collection<LogRecord> logRecords) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (LogRecord record : logRecords) {
|
||||
String formatted = formatter.format(record);
|
||||
sb.append(formatted).append('\n');
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private final Object lock = new Object();
|
||||
private final DateFormat dateFormat; // Locking: lock
|
||||
private final Date date; // Locking: lock
|
||||
@@ -0,0 +1,44 @@
|
||||
package org.briarproject.bramble.logging;
|
||||
|
||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||
import org.briarproject.bramble.api.system.TaskScheduler;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.logging.Formatter;
|
||||
import java.util.logging.LogRecord;
|
||||
import java.util.logging.StreamHandler;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
|
||||
class FlushingStreamHandler extends StreamHandler {
|
||||
|
||||
private static final int FLUSH_DELAY_MS = 5_000;
|
||||
|
||||
private final TaskScheduler scheduler;
|
||||
private final Executor ioExecutor;
|
||||
private final AtomicBoolean flushScheduled = new AtomicBoolean(false);
|
||||
|
||||
FlushingStreamHandler(TaskScheduler scheduler,
|
||||
Executor ioExecutor, OutputStream out, Formatter formatter) {
|
||||
super(out, formatter);
|
||||
this.scheduler = scheduler;
|
||||
this.ioExecutor = ioExecutor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void publish(LogRecord record) {
|
||||
super.publish(record);
|
||||
if (!flushScheduled.getAndSet(true)) {
|
||||
scheduler.schedule(this::scheduledFlush, ioExecutor,
|
||||
FLUSH_DELAY_MS, MILLISECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
@IoExecutor
|
||||
private void scheduledFlush() {
|
||||
flushScheduled.set(false);
|
||||
flush();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package org.briarproject.bramble.logging;
|
||||
|
||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.bramble.api.logging.PersistentLogManager;
|
||||
|
||||
import java.util.logging.Formatter;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
|
||||
@Module
|
||||
public class LoggingModule {
|
||||
|
||||
@Provides
|
||||
Formatter provideFormatter() {
|
||||
return new BriefLogFormatter();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
PersistentLogManager providePersistentLogManager(
|
||||
LifecycleManager lifecycleManager,
|
||||
PersistentLogManagerImpl persistentLogManager) {
|
||||
lifecycleManager.registerOpenDatabaseHook(persistentLogManager);
|
||||
return persistentLogManager;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
package org.briarproject.bramble.logging;
|
||||
|
||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.db.Transaction;
|
||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager.OpenDatabaseHook;
|
||||
import org.briarproject.bramble.api.lifecycle.ShutdownManager;
|
||||
import org.briarproject.bramble.api.logging.PersistentLogManager;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.settings.Settings;
|
||||
import org.briarproject.bramble.api.system.TaskScheduler;
|
||||
import org.briarproject.bramble.api.transport.StreamReaderFactory;
|
||||
import org.briarproject.bramble.api.transport.StreamWriter;
|
||||
import org.briarproject.bramble.api.transport.StreamWriterFactory;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Scanner;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.logging.Formatter;
|
||||
import java.util.logging.Handler;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.logging.StreamHandler;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.concurrent.ThreadSafe;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
|
||||
@ThreadSafe
|
||||
@NotNullByDefault
|
||||
class PersistentLogManagerImpl implements PersistentLogManager,
|
||||
OpenDatabaseHook {
|
||||
|
||||
private static final Logger LOG =
|
||||
getLogger(PersistentLogManagerImpl.class.getName());
|
||||
|
||||
private static final String LOG_FILE = "briar.log";
|
||||
private static final String OLD_LOG_FILE = "briar.log.old";
|
||||
|
||||
private final TaskScheduler scheduler;
|
||||
private final Executor ioExecutor;
|
||||
private final ShutdownManager shutdownManager;
|
||||
private final DatabaseComponent db;
|
||||
private final StreamReaderFactory streamReaderFactory;
|
||||
private final StreamWriterFactory streamWriterFactory;
|
||||
private final Formatter formatter;
|
||||
private final SecretKey logKey;
|
||||
private final AtomicReference<Integer> shutdownHookHandle =
|
||||
new AtomicReference<>();
|
||||
|
||||
@Nullable
|
||||
private volatile SecretKey oldLogKey = null;
|
||||
|
||||
@Inject
|
||||
PersistentLogManagerImpl(
|
||||
TaskScheduler scheduler,
|
||||
@IoExecutor Executor ioExecutor,
|
||||
ShutdownManager shutdownManager,
|
||||
DatabaseComponent db,
|
||||
StreamReaderFactory streamReaderFactory,
|
||||
StreamWriterFactory streamWriterFactory,
|
||||
Formatter formatter,
|
||||
CryptoComponent crypto) {
|
||||
this.scheduler = scheduler;
|
||||
this.ioExecutor = ioExecutor;
|
||||
this.shutdownManager = shutdownManager;
|
||||
this.db = db;
|
||||
this.streamReaderFactory = streamReaderFactory;
|
||||
this.streamWriterFactory = streamWriterFactory;
|
||||
this.formatter = formatter;
|
||||
logKey = crypto.generateSecretKey();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDatabaseOpened(Transaction txn) throws DbException {
|
||||
Settings s = db.getSettings(txn, LOG_SETTINGS_NAMESPACE);
|
||||
// Load the old log key, if any
|
||||
byte[] oldKeyBytes = s.getBytes(LOG_KEY_KEY);
|
||||
if (oldKeyBytes != null && oldKeyBytes.length == SecretKey.LENGTH) {
|
||||
LOG.info("Loaded old log key");
|
||||
oldLogKey = new SecretKey(oldKeyBytes);
|
||||
}
|
||||
// Store the current log key
|
||||
s.putBytes(LOG_KEY_KEY, logKey.getBytes());
|
||||
db.mergeSettings(txn, s, LOG_SETTINGS_NAMESPACE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Handler createLogHandler(File dir) throws IOException {
|
||||
File logFile = new File(dir, LOG_FILE);
|
||||
File oldLogFile = new File(dir, OLD_LOG_FILE);
|
||||
if (oldLogFile.exists() && !oldLogFile.delete())
|
||||
LOG.warning("Failed to delete old log file");
|
||||
if (logFile.exists() && !logFile.renameTo(oldLogFile))
|
||||
LOG.warning("Failed to rename log file");
|
||||
try {
|
||||
OutputStream out = new FileOutputStream(logFile);
|
||||
StreamWriter writer =
|
||||
streamWriterFactory.createLogStreamWriter(out, logKey);
|
||||
StreamHandler handler = new FlushingStreamHandler(scheduler,
|
||||
ioExecutor, writer.getOutputStream(), formatter);
|
||||
// Flush the log and terminate the stream at shutdown
|
||||
Runnable shutdownHook = () -> {
|
||||
LOG.info("Shutting down");
|
||||
handler.flush();
|
||||
try {
|
||||
writer.sendEndOfStream();
|
||||
} catch (IOException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
}
|
||||
};
|
||||
int handle = shutdownManager.addShutdownHook(shutdownHook);
|
||||
// If a previous handler registered a shutdown hook, remove it
|
||||
Integer oldHandle = shutdownHookHandle.getAndSet(handle);
|
||||
if (oldHandle != null) {
|
||||
shutdownManager.removeShutdownHook(oldHandle);
|
||||
}
|
||||
return handler;
|
||||
} catch (SecurityException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addLogHandler(File dir, Logger logger) throws IOException {
|
||||
for (Handler h : logger.getHandlers()) {
|
||||
if (h instanceof FlushingStreamHandler) logger.removeHandler(h);
|
||||
}
|
||||
logger.addHandler(createLogHandler(dir));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Scanner getPersistentLog(File dir, boolean old)
|
||||
throws IOException {
|
||||
if (old) {
|
||||
SecretKey oldLogKey = this.oldLogKey;
|
||||
if (oldLogKey == null) {
|
||||
LOG.info("Old log key has not been loaded");
|
||||
return emptyScanner();
|
||||
}
|
||||
return getPersistentLog(new File(dir, OLD_LOG_FILE), oldLogKey);
|
||||
} else {
|
||||
return getPersistentLog(new File(dir, LOG_FILE), logKey);
|
||||
}
|
||||
}
|
||||
|
||||
private Scanner getPersistentLog(File logFile, SecretKey key)
|
||||
throws IOException {
|
||||
if (logFile.exists()) {
|
||||
LOG.info("Reading log file");
|
||||
InputStream in = new FileInputStream(logFile);
|
||||
return new Scanner(streamReaderFactory.createLogStreamReader(in,
|
||||
key));
|
||||
} else {
|
||||
LOG.info("Log file does not exist");
|
||||
return emptyScanner();
|
||||
}
|
||||
}
|
||||
|
||||
private Scanner emptyScanner() {
|
||||
return new Scanner(new ByteArrayInputStream(new byte[0]));
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,11 @@ public class TestFeatureFlagModule {
|
||||
public boolean shouldEnableDisappearingMessages() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldEnablePersistentLogs() {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,7 +136,7 @@ dependencies {
|
||||
testImplementation 'androidx.fragment:fragment-testing:1.3.4'
|
||||
testImplementation "androidx.arch.core:core-testing:2.1.0"
|
||||
testImplementation "androidx.test.espresso:espresso-core:$espressoVersion"
|
||||
testImplementation 'org.robolectric:robolectric:4.7.2'
|
||||
testImplementation 'org.robolectric:robolectric:4.3.1'
|
||||
testImplementation 'org.mockito:mockito-core:3.9.0'
|
||||
testImplementation "junit:junit:$junit_version"
|
||||
testImplementation "org.jmock:jmock:$jmock_version"
|
||||
|
||||
@@ -3,9 +3,11 @@ package org.briarproject.bramble.account;
|
||||
import android.app.Application;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
import org.briarproject.bramble.api.FeatureFlags;
|
||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
import org.briarproject.bramble.api.db.DatabaseConfig;
|
||||
import org.briarproject.bramble.api.identity.IdentityManager;
|
||||
import org.briarproject.bramble.api.logging.PersistentLogManager;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.Localizer;
|
||||
import org.briarproject.briar.android.util.UiUtils;
|
||||
@@ -15,10 +17,16 @@ import javax.inject.Inject;
|
||||
class BriarAccountManager extends AndroidAccountManager {
|
||||
|
||||
@Inject
|
||||
BriarAccountManager(DatabaseConfig databaseConfig, CryptoComponent crypto,
|
||||
IdentityManager identityManager, SharedPreferences prefs,
|
||||
BriarAccountManager(
|
||||
DatabaseConfig databaseConfig,
|
||||
CryptoComponent crypto,
|
||||
IdentityManager identityManager,
|
||||
SharedPreferences prefs,
|
||||
PersistentLogManager logManager,
|
||||
FeatureFlags featureFlags,
|
||||
Application app) {
|
||||
super(databaseConfig, crypto, identityManager, prefs, app);
|
||||
super(databaseConfig, crypto, identityManager, prefs, logManager,
|
||||
featureFlags, app);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -22,6 +22,7 @@ import org.briarproject.bramble.api.keyagreement.PayloadEncoder;
|
||||
import org.briarproject.bramble.api.keyagreement.PayloadParser;
|
||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.bramble.api.logging.PersistentLogManager;
|
||||
import org.briarproject.bramble.api.plugin.PluginManager;
|
||||
import org.briarproject.bramble.api.settings.SettingsManager;
|
||||
import org.briarproject.bramble.api.system.AndroidExecutor;
|
||||
@@ -78,6 +79,7 @@ import org.briarproject.briar.api.privategroup.invitation.GroupInvitationManager
|
||||
import org.briarproject.briar.api.test.TestDataCreator;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.logging.Formatter;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
@@ -204,6 +206,10 @@ public interface AndroidComponent
|
||||
|
||||
AutoDeleteManager autoDeleteManager();
|
||||
|
||||
PersistentLogManager persistentLogManager();
|
||||
|
||||
Formatter formatter();
|
||||
|
||||
void inject(SignInReminderReceiver briarService);
|
||||
|
||||
void inject(BriarService briarService);
|
||||
|
||||
@@ -245,8 +245,9 @@ public class AppModule {
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getLogcatFile() {
|
||||
return AndroidUtils.getLogcatFile(app.getApplicationContext());
|
||||
public File getTemporaryLogFile() {
|
||||
return AndroidUtils
|
||||
.getTemporaryLogFile(app.getApplicationContext());
|
||||
}
|
||||
};
|
||||
return devConfig;
|
||||
@@ -337,6 +338,11 @@ public class AppModule {
|
||||
public boolean shouldEnableDisappearingMessages() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldEnablePersistentLogs() {
|
||||
return IS_DEBUG_BUILD;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,11 +17,14 @@ import com.vanniktech.emoji.google.GoogleEmojiProvider;
|
||||
import org.briarproject.bramble.BrambleAndroidEagerSingletons;
|
||||
import org.briarproject.bramble.BrambleAppComponent;
|
||||
import org.briarproject.bramble.BrambleCoreEagerSingletons;
|
||||
import org.briarproject.bramble.api.logging.PersistentLogManager;
|
||||
import org.briarproject.briar.BriarCoreEagerSingletons;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.logging.CachingLogHandler;
|
||||
import org.briarproject.briar.android.util.UiUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.Thread.UncaughtExceptionHandler;
|
||||
import java.util.logging.Handler;
|
||||
import java.util.logging.Logger;
|
||||
@@ -31,7 +34,10 @@ import androidx.annotation.NonNull;
|
||||
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
|
||||
import static java.util.logging.Level.FINE;
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
import static org.briarproject.bramble.util.AndroidUtils.getPersistentLogDir;
|
||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
import static org.briarproject.briar.android.TestingConstants.IS_DEBUG_BUILD;
|
||||
|
||||
public class BriarApplicationImpl extends Application
|
||||
@@ -81,6 +87,17 @@ public class BriarApplicationImpl extends Application
|
||||
rootLogger.addHandler(logHandler);
|
||||
rootLogger.setLevel(IS_DEBUG_BUILD ? FINE : INFO);
|
||||
|
||||
if (applicationComponent.featureFlags().shouldEnablePersistentLogs()) {
|
||||
PersistentLogManager logManager =
|
||||
applicationComponent.persistentLogManager();
|
||||
File logDir = getPersistentLogDir(this);
|
||||
try {
|
||||
rootLogger.addHandler(logManager.createLogHandler(logDir));
|
||||
} catch (IOException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
}
|
||||
}
|
||||
|
||||
LOG.info("Created");
|
||||
|
||||
EmojiManager.install(new GoogleEmojiProvider());
|
||||
|
||||
@@ -8,8 +8,9 @@ import androidx.annotation.Nullable;
|
||||
@NotNullByDefault
|
||||
public interface LogDecrypter {
|
||||
/**
|
||||
* Returns decrypted log records from {@link AndroidUtils#getLogcatFile}
|
||||
* or null if there was an error reading the logs.
|
||||
* Returns decrypted log records from
|
||||
* {@link AndroidUtils#getTemporaryLogFile} or null if there was an error
|
||||
* reading the logs.
|
||||
*/
|
||||
@Nullable
|
||||
String decryptLogs(@Nullable byte[] logKey);
|
||||
|
||||
@@ -41,7 +41,7 @@ class LogDecrypterImpl implements LogDecrypter {
|
||||
public String decryptLogs(@Nullable byte[] logKey) {
|
||||
if (logKey == null) return null;
|
||||
SecretKey key = new SecretKey(logKey);
|
||||
File logFile = devConfig.getLogcatFile();
|
||||
File logFile = devConfig.getTemporaryLogFile();
|
||||
try (InputStream in = new FileInputStream(logFile)) {
|
||||
InputStream reader =
|
||||
streamReaderFactory.createLogStreamReader(in, key);
|
||||
|
||||
@@ -8,7 +8,7 @@ import androidx.annotation.Nullable;
|
||||
@NotNullByDefault
|
||||
public interface LogEncrypter {
|
||||
/**
|
||||
* Writes encrypted log records to {@link AndroidUtils#getLogcatFile}
|
||||
* Writes encrypted log records to {@link AndroidUtils#getTemporaryLogFile}
|
||||
* and returns the encryption key if everything went fine.
|
||||
*/
|
||||
@Nullable
|
||||
|
||||
@@ -33,16 +33,19 @@ class LogEncrypterImpl implements LogEncrypter {
|
||||
|
||||
private final DevConfig devConfig;
|
||||
private final CachingLogHandler logHandler;
|
||||
private final Formatter formatter;
|
||||
private final CryptoComponent crypto;
|
||||
private final StreamWriterFactory streamWriterFactory;
|
||||
|
||||
@Inject
|
||||
LogEncrypterImpl(DevConfig devConfig,
|
||||
CachingLogHandler logHandler,
|
||||
Formatter formatter,
|
||||
CryptoComponent crypto,
|
||||
StreamWriterFactory streamWriterFactory) {
|
||||
this.devConfig = devConfig;
|
||||
this.logHandler = logHandler;
|
||||
this.formatter = formatter;
|
||||
this.crypto = crypto;
|
||||
this.streamWriterFactory = streamWriterFactory;
|
||||
}
|
||||
@@ -51,7 +54,7 @@ class LogEncrypterImpl implements LogEncrypter {
|
||||
@Override
|
||||
public byte[] encryptLogs() {
|
||||
SecretKey logKey = crypto.generateSecretKey();
|
||||
File logFile = devConfig.getLogcatFile();
|
||||
File logFile = devConfig.getTemporaryLogFile();
|
||||
try (OutputStream out = new FileOutputStream(logFile)) {
|
||||
StreamWriter streamWriter =
|
||||
streamWriterFactory.createLogStreamWriter(out, logKey);
|
||||
@@ -67,10 +70,8 @@ class LogEncrypterImpl implements LogEncrypter {
|
||||
}
|
||||
|
||||
private void writeLogString(Writer writer) throws IOException {
|
||||
Formatter formatter = new BriefLogFormatter();
|
||||
for (LogRecord record : logHandler.getRecentLogRecords()) {
|
||||
String formatted = formatter.format(record);
|
||||
writer.append(formatted).append('\n');
|
||||
writer.append(formatter.format(record)).append('\n');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.briar.BuildConfig;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.reporting.ReportData.MultiReportInfo;
|
||||
import org.briarproject.briar.android.reporting.ReportData.ReportInfo;
|
||||
import org.briarproject.briar.android.reporting.ReportData.ReportItem;
|
||||
import org.briarproject.briar.android.reporting.ReportData.SingleReportInfo;
|
||||
|
||||
@@ -71,7 +72,7 @@ class BriarReportCollector {
|
||||
}
|
||||
|
||||
ReportData collectReportData(@Nullable Throwable t, long appStartTime,
|
||||
String logs) {
|
||||
ReportInfo logs) {
|
||||
ReportData reportData = new ReportData()
|
||||
.add(getBasicInfo(t))
|
||||
.add(getDeviceInfo());
|
||||
@@ -82,7 +83,7 @@ class BriarReportCollector {
|
||||
.add(getStorage())
|
||||
.add(getConnectivity())
|
||||
.add(getBuildConfig())
|
||||
.add(getLogcat(logs))
|
||||
.add(getLogs(logs))
|
||||
.add(getDeviceFeatures());
|
||||
}
|
||||
|
||||
@@ -309,8 +310,8 @@ class BriarReportCollector {
|
||||
buildConfig);
|
||||
}
|
||||
|
||||
private ReportItem getLogcat(String logs) {
|
||||
return new ReportItem("Logcat", R.string.dev_report_logcat, logs);
|
||||
private ReportItem getLogs(ReportInfo logs) {
|
||||
return new ReportItem("Logs", R.string.dev_report_logcat, logs);
|
||||
}
|
||||
|
||||
private ReportItem getDeviceFeatures() {
|
||||
|
||||
@@ -4,6 +4,8 @@ import android.app.Application;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
|
||||
import org.briarproject.bramble.api.FeatureFlags;
|
||||
import org.briarproject.bramble.api.logging.PersistentLogManager;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.plugin.Plugin;
|
||||
import org.briarproject.bramble.api.plugin.PluginManager;
|
||||
@@ -11,7 +13,6 @@ import org.briarproject.bramble.api.plugin.TorConstants;
|
||||
import org.briarproject.bramble.api.reporting.DevReporter;
|
||||
import org.briarproject.bramble.util.AndroidUtils;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.logging.BriefLogFormatter;
|
||||
import org.briarproject.briar.android.logging.CachingLogHandler;
|
||||
import org.briarproject.briar.android.logging.LogDecrypter;
|
||||
import org.briarproject.briar.android.reporting.ReportData.MultiReportInfo;
|
||||
@@ -22,6 +23,9 @@ import org.json.JSONException;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Scanner;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Formatter;
|
||||
import java.util.logging.Logger;
|
||||
@@ -39,18 +43,24 @@ import static java.util.Objects.requireNonNull;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE;
|
||||
import static org.briarproject.bramble.util.AndroidUtils.getPersistentLogDir;
|
||||
import static org.briarproject.bramble.util.LogUtils.formatLog;
|
||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
|
||||
import static org.briarproject.briar.android.logging.BriefLogFormatter.formatLog;
|
||||
|
||||
@NotNullByDefault
|
||||
class ReportViewModel extends AndroidViewModel {
|
||||
|
||||
private static final int MAX_PERSISTENT_LOG_LINES = 1000;
|
||||
|
||||
private static final Logger LOG =
|
||||
getLogger(ReportViewModel.class.getName());
|
||||
|
||||
private final CachingLogHandler logHandler;
|
||||
private final LogDecrypter logDecrypter;
|
||||
private final Formatter formatter;
|
||||
private final PersistentLogManager logManager;
|
||||
private final FeatureFlags featureFlags;
|
||||
private final BriarReportCollector collector;
|
||||
private final DevReporter reporter;
|
||||
private final PluginManager pluginManager;
|
||||
@@ -71,12 +81,18 @@ class ReportViewModel extends AndroidViewModel {
|
||||
ReportViewModel(@NonNull Application application,
|
||||
CachingLogHandler logHandler,
|
||||
LogDecrypter logDecrypter,
|
||||
Formatter formatter,
|
||||
PersistentLogManager logManager,
|
||||
FeatureFlags featureFlags,
|
||||
DevReporter reporter,
|
||||
PluginManager pluginManager) {
|
||||
super(application);
|
||||
collector = new BriarReportCollector(application);
|
||||
this.logHandler = logHandler;
|
||||
this.logDecrypter = logDecrypter;
|
||||
this.formatter = formatter;
|
||||
this.logManager = logManager;
|
||||
this.featureFlags = featureFlags;
|
||||
this.reporter = reporter;
|
||||
this.pluginManager = pluginManager;
|
||||
}
|
||||
@@ -86,22 +102,30 @@ class ReportViewModel extends AndroidViewModel {
|
||||
this.initialComment = initialComment;
|
||||
isFeedback = t == null;
|
||||
if (reportData.getValue() == null) new SingleShotAndroidExecutor(() -> {
|
||||
String decryptedLogs;
|
||||
String currentLog;
|
||||
if (isFeedback) {
|
||||
Formatter formatter = new BriefLogFormatter();
|
||||
decryptedLogs =
|
||||
formatLog(formatter, logHandler.getRecentLogRecords());
|
||||
// We're in the main process, so get the log for this process
|
||||
currentLog = formatLog(formatter,
|
||||
logHandler.getRecentLogRecords());
|
||||
} else {
|
||||
decryptedLogs = logDecrypter.decryptLogs(logKey);
|
||||
if (decryptedLogs == null) {
|
||||
// We're in the crash reporter process, so try to load
|
||||
// the encrypted log that was saved by the main process
|
||||
currentLog = logDecrypter.decryptLogs(logKey);
|
||||
if (currentLog == null) {
|
||||
// error decrypting logs, get logs from this process
|
||||
Formatter formatter = new BriefLogFormatter();
|
||||
decryptedLogs = formatLog(formatter,
|
||||
currentLog = formatLog(formatter,
|
||||
logHandler.getRecentLogRecords());
|
||||
}
|
||||
}
|
||||
MultiReportInfo logs = new MultiReportInfo();
|
||||
logs.add("Current", currentLog);
|
||||
if (isFeedback && featureFlags.shouldEnablePersistentLogs()) {
|
||||
// Add persistent logs for the current and previous processes
|
||||
logs.add("Persistent", getPersistentLog(false));
|
||||
logs.add("PersistentOld", getPersistentLog(true));
|
||||
}
|
||||
ReportData data =
|
||||
collector.collectReportData(t, appStartTime, decryptedLogs);
|
||||
collector.collectReportData(t, appStartTime, logs);
|
||||
reportData.postValue(data);
|
||||
}).start();
|
||||
}
|
||||
@@ -226,6 +250,27 @@ class ReportViewModel extends AndroidViewModel {
|
||||
return closeReport;
|
||||
}
|
||||
|
||||
private String getPersistentLog(boolean old) {
|
||||
File logDir = getPersistentLogDir(getApplication());
|
||||
StringBuilder sb = new StringBuilder();
|
||||
try {
|
||||
Scanner scanner = logManager.getPersistentLog(logDir, old);
|
||||
LinkedList<String> lines = new LinkedList<>();
|
||||
int numLines = 0;
|
||||
while (scanner.hasNextLine()) {
|
||||
lines.add(scanner.nextLine());
|
||||
// If there are too many lines, return the most recent ones
|
||||
if (numLines == MAX_PERSISTENT_LOG_LINES) lines.pollFirst();
|
||||
else numLines++;
|
||||
}
|
||||
scanner.close();
|
||||
for (String line : lines) sb.append(line).append('\n');
|
||||
} catch (IOException e) {
|
||||
sb.append("Could not recover persistent log: ").append(e);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
// Used for a new thread as the Android executor thread may have died
|
||||
private static class SingleShotAndroidExecutor extends Thread {
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import android.view.View;
|
||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.util.ActivityLaunchers.CreateDocumentAdvanced;
|
||||
import org.briarproject.briar.android.util.ActivityLaunchers.GetImageAdvanced;
|
||||
|
||||
import javax.inject.Inject;
|
||||
@@ -21,6 +22,7 @@ import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceFragmentCompat;
|
||||
import androidx.preference.PreferenceGroup;
|
||||
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static org.briarproject.briar.android.AppModule.getAndroidComponent;
|
||||
import static org.briarproject.briar.android.TestingConstants.IS_DEBUG_BUILD;
|
||||
@@ -37,6 +39,10 @@ public class SettingsFragment extends PreferenceFragmentCompat {
|
||||
private static final String PREF_KEY_DEV = "pref_key_dev";
|
||||
private static final String PREF_KEY_EXPLODE = "pref_key_explode";
|
||||
private static final String PREF_KEY_SHARE_APP = "pref_key_share_app";
|
||||
private static final String PREF_KEY_EXPORT_LOG = "pref_key_export_log";
|
||||
private static final String PREF_EXPORT_OLD_LOG = "pref_key_export_old_log";
|
||||
|
||||
private static final String LOG_EXPORT_FILENAME = "briar-log.txt";
|
||||
|
||||
@Inject
|
||||
ViewModelProvider.Factory viewModelFactory;
|
||||
@@ -44,10 +50,18 @@ public class SettingsFragment extends PreferenceFragmentCompat {
|
||||
private SettingsViewModel viewModel;
|
||||
private AvatarPreference prefAvatar;
|
||||
|
||||
private final ActivityResultLauncher<String> launcher =
|
||||
private final ActivityResultLauncher<String> imageLauncher =
|
||||
registerForActivityResult(new GetImageAdvanced(),
|
||||
this::onImageSelected);
|
||||
|
||||
private final ActivityResultLauncher<String> logLauncher =
|
||||
registerForActivityResult(new CreateDocumentAdvanced(),
|
||||
uri -> onLogFileSelected(false, uri));
|
||||
|
||||
private final ActivityResultLauncher<String> oldLogLauncher =
|
||||
registerForActivityResult(new CreateDocumentAdvanced(),
|
||||
uri -> onLogFileSelected(true, uri));
|
||||
|
||||
@Override
|
||||
public void onAttach(@NonNull Context context) {
|
||||
super.onAttach(context);
|
||||
@@ -63,7 +77,7 @@ public class SettingsFragment extends PreferenceFragmentCompat {
|
||||
prefAvatar = requireNonNull(findPreference(PREF_KEY_AVATAR));
|
||||
if (viewModel.shouldEnableProfilePictures()) {
|
||||
prefAvatar.setOnPreferenceClickListener(preference -> {
|
||||
launcher.launch("image/*");
|
||||
imageLauncher.launch("image/*");
|
||||
return true;
|
||||
});
|
||||
} else {
|
||||
@@ -77,11 +91,32 @@ public class SettingsFragment extends PreferenceFragmentCompat {
|
||||
return true;
|
||||
});
|
||||
|
||||
Preference explode = requireNonNull(findPreference(PREF_KEY_EXPLODE));
|
||||
if (IS_DEBUG_BUILD) {
|
||||
Preference explode =
|
||||
requireNonNull(findPreference(PREF_KEY_EXPLODE));
|
||||
explode.setOnPreferenceClickListener(preference -> {
|
||||
throw new RuntimeException("Boom!");
|
||||
});
|
||||
Preference exportLog =
|
||||
requireNonNull(findPreference(PREF_KEY_EXPORT_LOG));
|
||||
if (SDK_INT >= 19) {
|
||||
exportLog.setOnPreferenceClickListener(preference -> {
|
||||
logLauncher.launch(LOG_EXPORT_FILENAME);
|
||||
return true;
|
||||
});
|
||||
} else {
|
||||
exportLog.setVisible(false);
|
||||
}
|
||||
Preference exportOldLog =
|
||||
requireNonNull(findPreference(PREF_EXPORT_OLD_LOG));
|
||||
if (SDK_INT >= 19) {
|
||||
exportOldLog.setOnPreferenceClickListener(preference -> {
|
||||
oldLogLauncher.launch(LOG_EXPORT_FILENAME);
|
||||
return true;
|
||||
});
|
||||
} else {
|
||||
exportOldLog.setVisible(false);
|
||||
}
|
||||
} else {
|
||||
PreferenceGroup dev = requireNonNull(findPreference(PREF_KEY_DEV));
|
||||
dev.setVisible(false);
|
||||
@@ -111,4 +146,7 @@ public class SettingsFragment extends PreferenceFragmentCompat {
|
||||
ConfirmAvatarDialogFragment.TAG);
|
||||
}
|
||||
|
||||
private void onLogFileSelected(boolean old, @Nullable Uri uri) {
|
||||
if (uri != null) viewModel.exportPersistentLog(old, uri);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import org.briarproject.bramble.api.identity.IdentityManager;
|
||||
import org.briarproject.bramble.api.identity.LocalAuthor;
|
||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.bramble.api.logging.PersistentLogManager;
|
||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||
import org.briarproject.bramble.api.plugin.BluetoothConstants;
|
||||
@@ -35,8 +36,12 @@ import org.briarproject.briar.api.avatar.AvatarManager;
|
||||
import org.briarproject.briar.api.identity.AuthorInfo;
|
||||
import org.briarproject.briar.api.identity.AuthorManager;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.Scanner;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@@ -50,6 +55,7 @@ import static android.widget.Toast.LENGTH_LONG;
|
||||
import static java.util.Arrays.asList;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
import static org.briarproject.bramble.util.AndroidUtils.getPersistentLogDir;
|
||||
import static org.briarproject.bramble.util.AndroidUtils.getSupportedImageContentTypes;
|
||||
import static org.briarproject.bramble.util.LogUtils.logDuration;
|
||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
@@ -78,6 +84,7 @@ class SettingsViewModel extends DbViewModel implements EventListener {
|
||||
private final ImageCompressor imageCompressor;
|
||||
private final Executor ioExecutor;
|
||||
private final FeatureFlags featureFlags;
|
||||
private final PersistentLogManager logManager;
|
||||
|
||||
final SettingsStore settingsStore;
|
||||
final TorSummaryProvider torSummaryProvider;
|
||||
@@ -108,7 +115,8 @@ class SettingsViewModel extends DbViewModel implements EventListener {
|
||||
LocationUtils locationUtils,
|
||||
CircumventionProvider circumventionProvider,
|
||||
@IoExecutor Executor ioExecutor,
|
||||
FeatureFlags featureFlags) {
|
||||
FeatureFlags featureFlags,
|
||||
PersistentLogManager logManager) {
|
||||
super(application, dbExecutor, lifecycleManager, db, androidExecutor);
|
||||
this.settingsManager = settingsManager;
|
||||
this.identityManager = identityManager;
|
||||
@@ -118,6 +126,7 @@ class SettingsViewModel extends DbViewModel implements EventListener {
|
||||
this.authorManager = authorManager;
|
||||
this.ioExecutor = ioExecutor;
|
||||
this.featureFlags = featureFlags;
|
||||
this.logManager = logManager;
|
||||
settingsStore = new SettingsStore(settingsManager, dbExecutor,
|
||||
SETTINGS_NAMESPACE);
|
||||
torSummaryProvider = new TorSummaryProvider(getApplication(),
|
||||
@@ -262,4 +271,38 @@ class SettingsViewModel extends DbViewModel implements EventListener {
|
||||
return screenLockTimeout;
|
||||
}
|
||||
|
||||
void exportPersistentLog(boolean old, Uri uri) {
|
||||
// We can use untranslated strings here, as this method is only called
|
||||
// in debug builds
|
||||
ioExecutor.execute(() -> {
|
||||
Application app = getApplication();
|
||||
try {
|
||||
OutputStream os =
|
||||
app.getContentResolver().openOutputStream(uri);
|
||||
if (os == null) throw new IOException();
|
||||
File logDir = getPersistentLogDir(app);
|
||||
Scanner scanner = logManager.getPersistentLog(logDir, old);
|
||||
if (!scanner.hasNextLine()) {
|
||||
scanner.close();
|
||||
androidExecutor.runOnUiThread(() ->
|
||||
Toast.makeText(app, "Log is empty",
|
||||
LENGTH_LONG).show());
|
||||
return;
|
||||
}
|
||||
PrintWriter w = new PrintWriter(os);
|
||||
while (scanner.hasNextLine()) w.println(scanner.nextLine());
|
||||
w.flush();
|
||||
w.close();
|
||||
scanner.close();
|
||||
androidExecutor.runOnUiThread(() ->
|
||||
Toast.makeText(app, "Log exported",
|
||||
LENGTH_LONG).show());
|
||||
} catch (IOException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
androidExecutor.runOnUiThread(() ->
|
||||
Toast.makeText(app, "Failed to export log",
|
||||
LENGTH_LONG).show());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,6 +58,14 @@
|
||||
android:targetPackage="@string/app_package" />
|
||||
</Preference>
|
||||
|
||||
<Preference
|
||||
android:key="pref_key_export_log"
|
||||
android:title="Export current log to SD card" />
|
||||
|
||||
<Preference
|
||||
android:key="pref_key_export_old_log"
|
||||
android:title="Export previous log to SD card" />
|
||||
|
||||
<Preference
|
||||
android:key="pref_key_explode"
|
||||
android:title="Crash" />
|
||||
|
||||
@@ -41,11 +41,7 @@ import static org.briarproject.briar.android.login.StrengthMeter.YELLOW;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@Config(sdk = 21,
|
||||
instrumentedPackages = {
|
||||
// required to access final members on androidx.loader.content.ModernAsyncTask
|
||||
"androidx.loader.content"
|
||||
})
|
||||
@Config(sdk = 21)
|
||||
public class SetupActivityTest {
|
||||
|
||||
@Rule
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.briarproject.briar.android.logging;
|
||||
|
||||
import org.briarproject.bramble.logging.BriefLogFormatter;
|
||||
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Test;
|
||||
@@ -15,8 +16,8 @@ import static java.util.logging.Level.FINE;
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Level.SEVERE;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static org.briarproject.bramble.util.LogUtils.formatLog;
|
||||
import static org.briarproject.bramble.util.StringUtils.getRandomString;
|
||||
import static org.briarproject.briar.android.logging.BriefLogFormatter.formatLog;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class LogEncryptionDecryptionTest extends BrambleMockTestCase {
|
||||
|
||||
@@ -42,7 +42,7 @@ class LoggingTestModule {
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getLogcatFile() {
|
||||
public File getTemporaryLogFile() {
|
||||
return logFile;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -35,11 +35,7 @@ import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(sdk = 21,
|
||||
instrumentedPackages = {
|
||||
// required to access final members on androidx.loader.content.ModernAsyncTask
|
||||
"androidx.loader.content"
|
||||
})
|
||||
@Config(sdk = 21)
|
||||
public class ChangePasswordActivityTest {
|
||||
|
||||
private ChangePasswordActivity changePasswordActivity;
|
||||
@@ -121,8 +117,7 @@ public class ChangePasswordActivityTest {
|
||||
verify(viewModel, times(1)).changePassword(eq(curPass), eq(safePass));
|
||||
// Return the result
|
||||
result.postEvent(SUCCESS);
|
||||
// TODO: this needs to be enabled again
|
||||
// assertTrue(changePasswordActivity.isFinishing());
|
||||
assertTrue(changePasswordActivity.isFinishing());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -36,8 +36,8 @@ import java.util.concurrent.Executor;
|
||||
|
||||
import androidx.arch.core.executor.testing.InstantTaskExecutorRule;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
import static java.util.Collections.singletonList;
|
||||
import static edu.emory.mathcs.backport.java.util.Collections.emptyList;
|
||||
import static edu.emory.mathcs.backport.java.util.Collections.singletonList;
|
||||
import static org.briarproject.bramble.test.TestUtils.getAuthor;
|
||||
import static org.briarproject.bramble.test.TestUtils.getContact;
|
||||
import static org.briarproject.bramble.test.TestUtils.getGroup;
|
||||
|
||||
@@ -57,7 +57,6 @@ dependencyVerification {
|
||||
'androidx.test.uiautomator:uiautomator:2.2.0:uiautomator-2.2.0.aar:2838e9d961dbffefbbd229a2bd4f6f82ac4fb2462975862a9e75e9ed325a3197',
|
||||
'androidx.test:core:1.3.0:core-1.3.0.aar:86549cae8c5b848f817e2c716e174c7dab61caf0b4df9848680eeb753089a337',
|
||||
'androidx.test:monitor:1.3.0:monitor-1.3.0.aar:f73a31306a783e63150c60c49e140dc38da39a1b7947690f4b73387b5ebad77e',
|
||||
'androidx.test:monitor:1.4.0:monitor-1.4.0.aar:46a912a1e175f27a97521af3f50e5af87c22c49275dd2c57c043740012806325',
|
||||
'androidx.test:orchestrator:1.3.0:orchestrator-1.3.0.apk:676f808d08a3d05050eae30c3b7d92ce5cef1e00a54d68355bb7e7d4b72366fe',
|
||||
'androidx.test:rules:1.3.0:rules-1.3.0.aar:c1753946c498b0d5d7cf341cfed661f66915c4c9deb4ed10462a08ae33b2429a',
|
||||
'androidx.test:runner:1.3.0:runner-1.3.0.aar:61d13f5a9fcbbd73ba18fa84e1d6a0111c6e1c665a89b418126966e61fffd93b',
|
||||
@@ -68,8 +67,10 @@ dependencyVerification {
|
||||
'androidx.versionedparcelable:versionedparcelable:1.1.0:versionedparcelable-1.1.0.aar:9a1d77140ac222b7866b5054ee7d159bc1800987ed2d46dd6afdd145abb710c1',
|
||||
'androidx.viewpager2:viewpager2:1.0.0:viewpager2-1.0.0.aar:e95c0031d4cc247cd48196c6287e58d2cee54d9c79b85afea7c90920330275af',
|
||||
'androidx.viewpager:viewpager:1.0.0:viewpager-1.0.0.aar:147af4e14a1984010d8f155e5e19d781f03c1d70dfed02a8e0d18428b8fc8682',
|
||||
'backport-util-concurrent:backport-util-concurrent:3.1:backport-util-concurrent-3.1.jar:f5759b7fcdfc83a525a036deedcbd32e5b536b625ebc282426f16ca137eb5902',
|
||||
'cglib:cglib:3.2.8:cglib-3.2.8.jar:3f64de999ecc5595dc84ca8ff0879d8a34c8623f9ef3c517a53ed59023fcb9db',
|
||||
'com.almworks.sqlite4java:sqlite4java:1.0.392:sqlite4java-1.0.392.jar:243a64470fda0e86a6fddeb0af4c7aa9426ce84e68cbfe18d75ee5da4b7e0b92',
|
||||
'classworlds:classworlds:1.1-alpha-2:classworlds-1.1-alpha-2.jar:2bf4e59f3acd106fea6145a9a88fe8956509f8b9c0fdd11eb96fee757269e3f3',
|
||||
'com.almworks.sqlite4java:sqlite4java:0.282:sqlite4java-0.282.jar:9e1d8dd83ca6003f841e3af878ce2dc7c22497493a7bb6d1b62ec1b0d0a83c05',
|
||||
'com.android.tools.analytics-library:protos:30.0.3:protos-30.0.3.jar:f62b89dcd9de719c6a7b7e15fb1dd20e45b57222e675cf633607bd0ed6bca7e7',
|
||||
'com.android.tools.analytics-library:shared:30.0.3:shared-30.0.3.jar:05aa9ba3cc890354108521fdf99802565aae5dd6ca44a6ac8bb8d594d1c1cd15',
|
||||
'com.android.tools.analytics-library:tracker:30.0.3:tracker-30.0.3.jar:5d0ef35bf6733e96210b5085a2a202152921bf834d345959dce1ca3369b528df',
|
||||
@@ -107,8 +108,10 @@ dependencyVerification {
|
||||
'com.github.javaparser:javaparser-core:3.17.0:javaparser-core-3.17.0.jar:23f5c982e1c7771423d37d52c774e8d2e80fd7ea7305ebe448797a96f67e6fca',
|
||||
'com.github.kobakei:MaterialFabSpeedDial:1.2.1:MaterialFabSpeedDial-1.2.1.aar:e86198c3c48cd832fb209a769a9f222c2a3cc045743b110ac2391d9737e3ea02',
|
||||
'com.google.android.apps.common.testing.accessibility.framework:accessibility-test-framework:2.0:accessibility-test-framework-2.0.jar:cdf16ef8f5b8023d003ce3cc1b0d51bda737762e2dab2fedf43d1c4292353f7f',
|
||||
'com.google.android.apps.common.testing.accessibility.framework:accessibility-test-framework:2.1:accessibility-test-framework-2.1.jar:7b0aa6ed7553597ce0610684a9f7eca8021eee218f2e2f427c04a7fbf5f920bd',
|
||||
'com.google.android.material:material:1.3.0:material-1.3.0.aar:cbf1e7d69fc236cdadcbd1ec5f6c0a1a41aca6ad1ef7f8481058956270ab1f0a',
|
||||
'com.google.auto.value:auto-value-annotations:1.7.4:auto-value-annotations-1.7.4.jar:fedd59b0b4986c342f6ab2d182f2a4ee9fceb2c7e2d5bdc4dc764c92394a23d3',
|
||||
'com.google.auto.service:auto-service:1.0-rc4:auto-service-1.0-rc4.jar:e422d49c312fd2031222e7306e8108c1b4118eb9c049f1b51eca280bed87e924',
|
||||
'com.google.auto:auto-common:0.8:auto-common-0.8.jar:97db1709f57b91b32edacb596ef4641872f227b7d99ad90e467f0d77f5ba134a',
|
||||
'com.google.code.findbugs:annotations:3.0.1:annotations-3.0.1.jar:6b47ff0a6de0ce17cbedc3abb0828ca5bce3009d53ea47b3723ff023c4742f79',
|
||||
'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7',
|
||||
'com.google.code.gson:gson:2.8.6:gson-2.8.6.jar:c8fb4839054d280b3033f800d1f5a97de2f028eb8ba2eb458ad287e536f3f25f',
|
||||
@@ -118,7 +121,6 @@ dependencyVerification {
|
||||
'com.google.dagger:dagger:2.33:dagger-2.33.jar:d8798c5b8cf6b125234e33af5c6293bb9f2208ce29b57924c35b8c0be7b6bdcb',
|
||||
'com.google.errorprone:error_prone_annotations:2.2.0:error_prone_annotations-2.2.0.jar:6ebd22ca1b9d8ec06d41de8d64e0596981d9607b42035f9ed374f9de271a481a',
|
||||
'com.google.errorprone:error_prone_annotations:2.3.4:error_prone_annotations-2.3.4.jar:baf7d6ea97ce606c53e11b6854ba5f2ce7ef5c24dddf0afa18d1260bd25b002c',
|
||||
'com.google.errorprone:error_prone_annotations:2.9.0:error_prone_annotations-2.9.0.jar:f947bdc33ae27a6b4aa44799e6c21e1944797bd0010ba43eb82d11446e163694',
|
||||
'com.google.errorprone:javac-shaded:9-dev-r4023-3:javac-shaded-9-dev-r4023-3.jar:65bfccf60986c47fbc17c9ebab0be626afc41741e0a6ec7109e0768817a36f30',
|
||||
'com.google.googlejavaformat:google-java-format:1.5:google-java-format-1.5.jar:aa19ad7850fb85178aa22f2fddb163b84d6ce4d0035872f30d4408195ca1144e',
|
||||
'com.google.guava:failureaccess:1.0.1:failureaccess-1.0.1.jar:a171ee4c734dd2da837e4b16be9df4661afab72a41adaf31eb84dfdaf936ca26',
|
||||
@@ -129,6 +131,7 @@ dependencyVerification {
|
||||
'com.google.j2objc:j2objc-annotations:1.1:j2objc-annotations-1.1.jar:2994a7eb78f2710bd3d3bfb639b2c94e219cedac0d4d084d516e78c16dddecf6',
|
||||
'com.google.j2objc:j2objc-annotations:1.3:j2objc-annotations-1.3.jar:21af30c92267bd6122c0e0b4d20cccb6641a37eaf956c6540ec471d584e64a7b',
|
||||
'com.google.jimfs:jimfs:1.1:jimfs-1.1.jar:c4828e28d7c0a930af9387510b3bada7daa5c04d7c25a75c7b8b081f1c257ddd',
|
||||
'com.google.protobuf:protobuf-java:2.6.1:protobuf-java-2.6.1.jar:55aa554843983f431df5616112cf688d38aa17c132357afd1c109435bfdac4e6',
|
||||
'com.google.protobuf:protobuf-java:3.10.0:protobuf-java-3.10.0.jar:161d7d61a8cb3970891c299578702fd079646e032329d6c2cabf998d191437c9',
|
||||
'com.google.zxing:core:3.3.3:core-3.3.3.jar:5820f81e943e4bce0329306621e2d6255d2930b0a6ce934c5c23c0d6d3f20599',
|
||||
'com.googlecode.json-simple:json-simple:1.1:json-simple-1.1.jar:2d9484f4c649f708f47f9a479465fc729770ee65617dca3011836602264f6439',
|
||||
@@ -159,6 +162,8 @@ dependencyVerification {
|
||||
'jline:jline:2.14.6:jline-2.14.6.jar:97d1acaac82409be42e622d7a54d3ae9d08517e8aefdea3d2ba9791150c2f02d',
|
||||
'junit:junit:4.13.1:junit-4.13.1.jar:c30719db974d6452793fe191b3638a5777005485bae145924044530ffa5f6122',
|
||||
'junit:junit:4.13.2:junit-4.13.2.jar:8e495b634469d64fb8acfa3495a065cbacc8a0fff55ce1e31007be4c16dc57d3',
|
||||
'nekohtml:nekohtml:1.9.6.2:nekohtml-1.9.6.2.jar:fdff6cfa9ed9cc911c842a5d2395f209ec621ef1239d46810e9e495809d3ae09',
|
||||
'nekohtml:xercesMinimal:1.9.6.2:xercesMinimal-1.9.6.2.jar:95b8b357d19f63797dd7d67622fd3f18374d64acbc6584faba1c7759a31e8438',
|
||||
'net.bytebuddy:byte-buddy-agent:1.10.20:byte-buddy-agent-1.10.20.jar:b592a6c43e752bf41659717956c57fbb790394d2ee5f8941876659f9c5c0e7e8',
|
||||
'net.bytebuddy:byte-buddy:1.10.20:byte-buddy-1.10.20.jar:5fcad05da791e9a22811c255a4a74b7ea094b7243d9dbf3e6fc578c8c94290ac',
|
||||
'net.java.dev.jna:jna-platform:5.6.0:jna-platform-5.6.0.jar:9ecea8bf2b1b39963939d18b70464eef60c508fed8820f9dcaba0c35518eabf7',
|
||||
@@ -171,11 +176,27 @@ dependencyVerification {
|
||||
'org.apache.ant:ant-antlr:1.10.9:ant-antlr-1.10.9.jar:7623dc9d0f20ea713290c6bf1a23f4c059447aef7ff9f5b2be75960f3f028d2e',
|
||||
'org.apache.ant:ant-junit:1.10.9:ant-junit-1.10.9.jar:960bdc8827954d62206ba42d0a68a7ee4476175ba47bb113e17e77cce7394630',
|
||||
'org.apache.ant:ant-launcher:1.10.9:ant-launcher-1.10.9.jar:fcce891f57f3be72149ff96ac2a80574165b3e0839866b95d24528f3027d50c1',
|
||||
'org.apache.ant:ant-launcher:1.8.0:ant-launcher-1.8.0.jar:da9fd92eacdf63daf0be52eb71accc10ff7943a85d7aac9ea96cf7e03ee3d3cc',
|
||||
'org.apache.ant:ant:1.10.9:ant-1.10.9.jar:0715478af585ea80a18985613ebecdc7922122d45b2c3c970ff9b352cddb75fc',
|
||||
'org.apache.ant:ant:1.8.0:ant-1.8.0.jar:0251dbb938740ace07a53675113eee753ba389db65aebc814b175af50321620e',
|
||||
'org.apache.commons:commons-compress:1.20:commons-compress-1.20.jar:0aeb625c948c697ea7b205156e112363b59ed5e2551212cd4e460bdb72c7c06e',
|
||||
'org.apache.httpcomponents:httpclient:4.5.6:httpclient-4.5.6.jar:c03f813195e7a80e3608d0ddd8da80b21696a4c92a6a2298865bf149071551c7',
|
||||
'org.apache.httpcomponents:httpcore:4.4.10:httpcore-4.4.10.jar:78ba1096561957db1b55200a159b648876430342d15d461277e62360da19f6fd',
|
||||
'org.apache.httpcomponents:httpmime:4.5.6:httpmime-4.5.6.jar:0b2b1102c18d3c7e05a77214b9b7501a6f6056174ae5604e0e256776eda7553e',
|
||||
'org.apache.maven.wagon:wagon-file:1.0-beta-6:wagon-file-1.0-beta-6.jar:7298feeb36ff14dd933c38e62585fb9973fea32fb3c4bc5379428cb1aac5dd3c',
|
||||
'org.apache.maven.wagon:wagon-http-lightweight:1.0-beta-6:wagon-http-lightweight-1.0-beta-6.jar:be214032de23c6b520b79c1ccdb160948e0c67ed7c11984b7ec4ca5537867b4e',
|
||||
'org.apache.maven.wagon:wagon-http-shared:1.0-beta-6:wagon-http-shared-1.0-beta-6.jar:f095c882716d49269a806685dcb256fa6a36389b2713ac56bb758bf8693565a2',
|
||||
'org.apache.maven.wagon:wagon-provider-api:1.0-beta-6:wagon-provider-api-1.0-beta-6.jar:e116f32edcb77067289a3148143f2c0c97b27cf9a1342f8108ee37dec4868861',
|
||||
'org.apache.maven:maven-ant-tasks:2.1.3:maven-ant-tasks-2.1.3.jar:f16b5ea711dfe0323454b880180aa832420ec039936e4aa75fb978748634808a',
|
||||
'org.apache.maven:maven-artifact-manager:2.2.1:maven-artifact-manager-2.2.1.jar:d1e247c4ed3952385fd704ac9db2a222247cfe7d20508b4f3c76b90f857952ed',
|
||||
'org.apache.maven:maven-artifact:2.2.1:maven-artifact-2.2.1.jar:d53062ffe8677a4f5e1ad3a1d1fa37ed600fab39166d39be7ed204635c5f839b',
|
||||
'org.apache.maven:maven-error-diagnostics:2.2.1:maven-error-diagnostics-2.2.1.jar:b3005544708f8583e455c22b09a4940596a057108bccdadb9db4d8e048091fed',
|
||||
'org.apache.maven:maven-model:2.2.1:maven-model-2.2.1.jar:153b32f474fd676ec36ad807c508885005139140fc92168bb76bf6be31f8efb8',
|
||||
'org.apache.maven:maven-plugin-registry:2.2.1:maven-plugin-registry-2.2.1.jar:4ad0673155d7e0e5cf6d13689802d8d507f38e5ea00a6d2fb92aef206108213d',
|
||||
'org.apache.maven:maven-profile:2.2.1:maven-profile-2.2.1.jar:ecaffef655fea6b138f0855a12f7dbb59fc0d6bffb5c1bfd31803cccb49ea08c',
|
||||
'org.apache.maven:maven-project:2.2.1:maven-project-2.2.1.jar:24ddb65b7a6c3befb6267ce5f739f237c84eba99389265c30df67c3dd8396a40',
|
||||
'org.apache.maven:maven-repository-metadata:2.2.1:maven-repository-metadata-2.2.1.jar:5fe283f47b0e7f7d95a4252af3fa7a0db4d8f080cd9df308608c0472b8f168a1',
|
||||
'org.apache.maven:maven-settings:2.2.1:maven-settings-2.2.1.jar:9a9f556713a404e770c9dbdaed7eb086078014c989291960c76fdde6db4192f7',
|
||||
'org.bouncycastle:bcpkix-jdk15on:1.56:bcpkix-jdk15on-1.56.jar:7043dee4e9e7175e93e0b36f45b1ec1ecb893c5f755667e8b916eb8dd201c6ca',
|
||||
'org.bouncycastle:bcprov-jdk15on:1.56:bcprov-jdk15on-1.56.jar:963e1ee14f808ffb99897d848ddcdb28fa91ddda867eb18d303e82728f878349',
|
||||
'org.bouncycastle:bcprov-jdk15on:1.69:bcprov-jdk15on-1.69.jar:e469bd39f936999f256002631003ff022a22951da9d5bd9789c7abfa9763a292',
|
||||
@@ -205,6 +226,9 @@ dependencyVerification {
|
||||
'org.codehaus.groovy:groovy-xml:3.0.7:groovy-xml-3.0.7.jar:8a62e7c9ddece3e82676c4bef2f2c100f459602cd1fb6a14e94187bf863e97ff',
|
||||
'org.codehaus.groovy:groovy:3.0.7:groovy-3.0.7.jar:51d1777e8dd1f00e60ea56e00d8a354ff5aab1f00fc8464ae8d39d71867e401f',
|
||||
'org.codehaus.mojo:animal-sniffer-annotations:1.17:animal-sniffer-annotations-1.17.jar:92654f493ecfec52082e76354f0ebf87648dc3d5cec2e3c3cdb947c016747a53',
|
||||
'org.codehaus.plexus:plexus-container-default:1.0-alpha-9-stable-1:plexus-container-default-1.0-alpha-9-stable-1.jar:7c758612888782ccfe376823aee7cdcc7e0cdafb097f7ef50295a0b0c3a16edf',
|
||||
'org.codehaus.plexus:plexus-interpolation:1.11:plexus-interpolation-1.11.jar:fd9507feb858fa620d1b4aa4b7039fdea1a77e09d3fd28cfbddfff468d9d8c28',
|
||||
'org.codehaus.plexus:plexus-utils:1.5.15:plexus-utils-1.5.15.jar:2ca121831e597b4d8f2cb22d17c5c041fc23a7777ceb6bfbdd4dfb34bbe7d997',
|
||||
'org.glassfish.jaxb:jaxb-runtime:2.3.2:jaxb-runtime-2.3.2.jar:e6e0a1e89fb6ff786279e6a0082d5cef52dc2ebe67053d041800737652b4fd1b',
|
||||
'org.glassfish.jaxb:txw2:2.3.2:txw2-2.3.2.jar:4a6a9f483388d461b81aa9a28c685b8b74c0597993bf1884b04eddbca95f48fe',
|
||||
'org.hamcrest:hamcrest-core:1.3:hamcrest-core-1.3.jar:66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9',
|
||||
@@ -245,27 +269,22 @@ dependencyVerification {
|
||||
'org.objenesis:objenesis:3.2:objenesis-3.2.jar:03d960bd5aef03c653eb000413ada15eb77cdd2b8e4448886edf5692805e35f3',
|
||||
'org.opentest4j:opentest4j:1.2.0:opentest4j-1.2.0.jar:58812de60898d976fb81ef3b62da05c6604c18fd4a249f5044282479fc286af2',
|
||||
'org.ow2.asm:asm-analysis:7.0:asm-analysis-7.0.jar:e981f8f650c4d900bb033650b18e122fa6b161eadd5f88978d08751f72ee8474',
|
||||
'org.ow2.asm:asm-analysis:9.2:asm-analysis-9.2.jar:878fbe521731c072d14d2d65b983b1beae6ad06fda0007b6a8bae81f73f433c4',
|
||||
'org.ow2.asm:asm-commons:7.0:asm-commons-7.0.jar:fed348ef05958e3e846a3ac074a12af5f7936ef3d21ce44a62c4fa08a771927d',
|
||||
'org.ow2.asm:asm-commons:9.2:asm-commons-9.2.jar:be4ce53138a238bb522cd781cf91f3ba5ce2f6ca93ec62d46a162a127225e0a6',
|
||||
'org.ow2.asm:asm-tree:7.0:asm-tree-7.0.jar:cfd7a0874f9de36a999c127feeadfbfe6e04d4a71ee954d7af3d853f0be48a6c',
|
||||
'org.ow2.asm:asm-tree:9.2:asm-tree-9.2.jar:aabf9bd23091a4ebfc109c1f3ee7cf3e4b89f6ba2d3f51c5243f16b3cffae011',
|
||||
'org.ow2.asm:asm-util:7.0:asm-util-7.0.jar:75fbbca440ef463f41c2b0ab1a80abe67e910ac486da60a7863cbcb5bae7e145',
|
||||
'org.ow2.asm:asm-util:9.2:asm-util-9.2.jar:ff5b3cd331ae8a9a804768280da98f50f424fef23dd3c788bb320e08c94ee598',
|
||||
'org.ow2.asm:asm:7.0:asm-7.0.jar:b88ef66468b3c978ad0c97fd6e90979e56155b4ac69089ba7a44e9aa7ffe9acf',
|
||||
'org.ow2.asm:asm:9.2:asm-9.2.jar:b9d4fe4d71938df38839f0eca42aaaa64cf8b313d678da036f0cb3ca199b47f5',
|
||||
'org.robolectric:annotations:4.7.2:annotations-4.7.2.jar:231821517a9a4fc8c577abd570fa48f3214da10e359083ecc4345bbb4e4a73c6',
|
||||
'org.robolectric:junit:4.7.2:junit-4.7.2.jar:27ffba8662c7d8e011ab243202af9dbab9e89f5dc2762514ab6539a1be27346f',
|
||||
'org.robolectric:nativeruntime:4.7.2:nativeruntime-4.7.2.jar:804bf799e17cf6358f8e11919add8cd0fa958abef5cb76003aa6f9a763802adf',
|
||||
'org.robolectric:pluginapi:4.7.2:pluginapi-4.7.2.jar:6414ef8ff549a3a195993df4798f70e8b31d81e0ffc6a0d47dc007aedfc2ee78',
|
||||
'org.robolectric:plugins-maven-dependency-resolver:4.7.2:plugins-maven-dependency-resolver-4.7.2.jar:660bb58d7dbba32efbbe19164ca1d0a81f19a6bec78bc7f21dd4248483bc997c',
|
||||
'org.robolectric:resources:4.7.2:resources-4.7.2.jar:6bd48f83fc9c5b50d49d49512ac6bd2f923d078d3140ed4ecdb50913d27d5b0c',
|
||||
'org.robolectric:robolectric:4.7.2:robolectric-4.7.2.jar:5bbdeb7647ea2377d40a5cfddb3fd53021741d15bbfcde825e9326f5f501b930',
|
||||
'org.robolectric:sandbox:4.7.2:sandbox-4.7.2.jar:f008e97cd35bdbc98a4ccbbe3bf3ff938e6b960c359943685f7233b02f67e811',
|
||||
'org.robolectric:shadowapi:4.7.2:shadowapi-4.7.2.jar:dba5a4b630997b2fa22ff3e67d6c168de4c2ad20570adce653eb9f7a014a2a8b',
|
||||
'org.robolectric:shadows-framework:4.7.2:shadows-framework-4.7.2.jar:423b726fad8e09c302ab92048e1f378440a9279f49dc6fa6dc4d4d5c66200e64',
|
||||
'org.robolectric:utils-reflector:4.7.2:utils-reflector-4.7.2.jar:28cc93927b48de95d3b392e7439817037099cf6327aa633c39735d0ed2da55a6',
|
||||
'org.robolectric:utils:4.7.2:utils-4.7.2.jar:45715d23d41ee0b6e667218d0f55b4f79bb160d83eaf836c8daa41bbcfe7f83b',
|
||||
'org.ow2.asm:asm:7.1:asm-7.1.jar:4ab2fa2b6d2cc9ccb1eaa05ea329c407b47b13ed2915f62f8c4b8cc96258d4de',
|
||||
'org.robolectric:annotations:4.3.1:annotations-4.3.1.jar:ce679af70c22620b5752aa6c1555d0653198d6370e9a93fe71b8eaaebc5ffaf6',
|
||||
'org.robolectric:junit:4.3.1:junit-4.3.1.jar:60c85ea7fd652bc4e57567cbd3c41c5d32f2c678e212b713cefa6c63570451ce',
|
||||
'org.robolectric:pluginapi:4.3.1:pluginapi-4.3.1.jar:229256a260a1d8e8d33613a3de7ccd639661a7061251c1974975ed427428b468',
|
||||
'org.robolectric:plugins-maven-dependency-resolver:4.3.1:plugins-maven-dependency-resolver-4.3.1.jar:0d6c577fdefe254659ffba5c0564d7e00c69f03e99a4ebb6c150419834cdb703',
|
||||
'org.robolectric:resources:4.3.1:resources-4.3.1.jar:93033237006b51541f8e93d65940f9040367775937d0ce9ac3f4ef72771c51b8',
|
||||
'org.robolectric:robolectric:4.3.1:robolectric-4.3.1.jar:3ef4267112ba581ee2a7ad37859bf61571404f07df85b8ad1da054f90eb57a5a',
|
||||
'org.robolectric:sandbox:4.3.1:sandbox-4.3.1.jar:405f73400d717e083b25af92fa7866a76765dd4e97cf7fd046023d4f05375a9f',
|
||||
'org.robolectric:shadowapi:4.3.1:shadowapi-4.3.1.jar:a63d13e7f3816f28ac33eea71a15c7f3f0053ecd01b08cc1e1e119af35ca1197',
|
||||
'org.robolectric:shadows-framework:4.3.1:shadows-framework-4.3.1.jar:9c69db134cdd79be751856a148020fd9b32b086bb491846eedc0a1106fcadd5e',
|
||||
'org.robolectric:utils-reflector:4.3.1:utils-reflector-4.3.1.jar:9d7bf2557947d44d6f3ed76ec5231e8b72e33eb61c65ac9e149ad307b0eb936c',
|
||||
'org.robolectric:utils:4.3.1:utils-4.3.1.jar:6f9e406cd667019a5450e473c4e2d372bff9c9ab6ef55aafcbc9843109cb1519',
|
||||
'org.testng:testng:7.3.0:testng-7.3.0.jar:63727488f9717d57f0d0a0fee5a1fc10a2be9cfcff2ec3a7187656d663c0774e',
|
||||
'tools.fastlane:screengrab:2.0.0:screengrab-2.0.0.aar:15ac15eb7c371db05e721be8d466567c2b7274b767d91478e781b6d89ee5d3d0',
|
||||
'uk.co.samuelwall:material-tap-target-prompt:3.3.0:material-tap-target-prompt-3.3.0.aar:00f16e8d7e55d01e3b41cf66e09eee8588870ca7285ba3c72267ca0482f1606e',
|
||||
|
||||
@@ -44,7 +44,6 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
@@ -55,15 +54,13 @@ import java.util.logging.Logger;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.concurrent.ThreadSafe;
|
||||
import javax.inject.Inject;
|
||||
import javax.net.SocketFactory;
|
||||
|
||||
import okhttp3.Dns;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
import okhttp3.ResponseBody;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
import static java.util.Collections.sort;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
import static org.briarproject.briar.api.blog.BlogConstants.MAX_BLOG_POST_TEXT_LENGTH;
|
||||
@@ -83,8 +80,6 @@ class FeedManagerImpl implements FeedManager, EventListener, OpenDatabaseHook,
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(FeedManagerImpl.class.getName());
|
||||
|
||||
private static final int CONNECT_TIMEOUT = 60 * 1000; // Milliseconds
|
||||
|
||||
private final TaskScheduler scheduler;
|
||||
private final Executor ioExecutor;
|
||||
private final DatabaseComponent db;
|
||||
@@ -93,9 +88,8 @@ class FeedManagerImpl implements FeedManager, EventListener, OpenDatabaseHook,
|
||||
private final BlogManager blogManager;
|
||||
private final BlogPostFactory blogPostFactory;
|
||||
private final FeedFactory feedFactory;
|
||||
private final SocketFactory torSocketFactory;
|
||||
private final Clock clock;
|
||||
private final Dns noDnsLookups;
|
||||
private final WeakSingletonProvider<OkHttpClient> httpClientProvider;
|
||||
private final AtomicBoolean fetcherStarted = new AtomicBoolean(false);
|
||||
|
||||
private volatile boolean torActive = false;
|
||||
@@ -109,9 +103,8 @@ class FeedManagerImpl implements FeedManager, EventListener, OpenDatabaseHook,
|
||||
BlogManager blogManager,
|
||||
BlogPostFactory blogPostFactory,
|
||||
FeedFactory feedFactory,
|
||||
SocketFactory torSocketFactory,
|
||||
Clock clock,
|
||||
Dns noDnsLookups) {
|
||||
WeakSingletonProvider<OkHttpClient> httpClientProvider,
|
||||
Clock clock) {
|
||||
this.scheduler = scheduler;
|
||||
this.ioExecutor = ioExecutor;
|
||||
this.db = db;
|
||||
@@ -120,9 +113,8 @@ class FeedManagerImpl implements FeedManager, EventListener, OpenDatabaseHook,
|
||||
this.blogManager = blogManager;
|
||||
this.blogPostFactory = blogPostFactory;
|
||||
this.feedFactory = feedFactory;
|
||||
this.torSocketFactory = torSocketFactory;
|
||||
this.httpClientProvider = httpClientProvider;
|
||||
this.clock = clock;
|
||||
this.noDnsLookups = noDnsLookups;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -364,19 +356,13 @@ class FeedManagerImpl implements FeedManager, EventListener, OpenDatabaseHook,
|
||||
}
|
||||
|
||||
private InputStream getFeedInputStream(String url) throws IOException {
|
||||
// Build HTTP Client
|
||||
OkHttpClient client = new OkHttpClient.Builder()
|
||||
.socketFactory(torSocketFactory)
|
||||
.dns(noDnsLookups) // Don't make local DNS lookups
|
||||
.connectTimeout(CONNECT_TIMEOUT, MILLISECONDS)
|
||||
.build();
|
||||
|
||||
// Build Request
|
||||
Request request = new Request.Builder()
|
||||
.url(url)
|
||||
.build();
|
||||
|
||||
// Execute Request
|
||||
OkHttpClient client = httpClientProvider.get();
|
||||
Response response = client.newCall(request).execute();
|
||||
ResponseBody body = response.body();
|
||||
if (body != null) return body.byteStream();
|
||||
@@ -399,7 +385,8 @@ class FeedManagerImpl implements FeedManager, EventListener, OpenDatabaseHook,
|
||||
long lastEntryTime = feed.getLastEntryTime();
|
||||
Transaction txn = db.startTransaction(false);
|
||||
try {
|
||||
Collections.sort(entries, getEntryComparator());
|
||||
//noinspection Java8ListSort
|
||||
sort(entries, getEntryComparator());
|
||||
for (SyndEntry entry : entries) {
|
||||
long entryTime;
|
||||
if (entry.getPublishedDate() != null) {
|
||||
|
||||
@@ -5,15 +5,23 @@ import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.briar.api.blog.BlogManager;
|
||||
import org.briarproject.briar.api.feed.FeedManager;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import javax.net.SocketFactory;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import okhttp3.Dns;
|
||||
import okhttp3.OkHttpClient;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
|
||||
@Module
|
||||
public class FeedModule {
|
||||
|
||||
private static final int CONNECT_TIMEOUT = 60_000; // Milliseconds
|
||||
|
||||
public static class EagerSingletons {
|
||||
@Inject
|
||||
FeedManager feedManager;
|
||||
@@ -35,4 +43,25 @@ public class FeedModule {
|
||||
return feedFactory;
|
||||
}
|
||||
|
||||
// Share an HTTP client instance between requests where possible, while
|
||||
// allowing the client to be garbage-collected between requests. The
|
||||
// provider keeps a weak reference to the last client instance and reuses
|
||||
// the instance until it gets garbage-collected. See
|
||||
// https://medium.com/@leandromazzuquini/if-you-are-using-okhttp-you-should-know-this-61d68e065a2b
|
||||
@Provides
|
||||
@Singleton
|
||||
WeakSingletonProvider<OkHttpClient> provideOkHttpClientProvider(
|
||||
SocketFactory torSocketFactory, Dns noDnsLookups) {
|
||||
return new WeakSingletonProvider<OkHttpClient>() {
|
||||
@Override
|
||||
@Nonnull
|
||||
public OkHttpClient createInstance() {
|
||||
return new OkHttpClient.Builder()
|
||||
.socketFactory(torSocketFactory)
|
||||
.dns(noDnsLookups) // Don't make local DNS lookups
|
||||
.connectTimeout(CONNECT_TIMEOUT, MILLISECONDS)
|
||||
.build();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package org.briarproject.briar.feed;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
import javax.annotation.concurrent.GuardedBy;
|
||||
import javax.inject.Provider;
|
||||
|
||||
/**
|
||||
* A {@link Provider} that keeps a {@link WeakReference} to the last provided
|
||||
* instance and provides the same instance again until the instance is garbage
|
||||
* collected.
|
||||
*/
|
||||
@NotNullByDefault
|
||||
abstract class WeakSingletonProvider<T> implements Provider<T> {
|
||||
|
||||
private final Object lock = new Object();
|
||||
@GuardedBy("lock")
|
||||
private WeakReference<T> ref = new WeakReference<>(null);
|
||||
|
||||
@Override
|
||||
public T get() {
|
||||
synchronized (lock) {
|
||||
T instance = ref.get();
|
||||
if (instance == null) {
|
||||
instance = createInstance();
|
||||
ref = new WeakReference<>(instance);
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
||||
abstract T createInstance();
|
||||
}
|
||||
@@ -33,10 +33,13 @@ import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.net.SocketFactory;
|
||||
|
||||
import okhttp3.Dns;
|
||||
import okhttp3.OkHttpClient;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
import static org.briarproject.bramble.test.TestUtils.getGroup;
|
||||
import static org.briarproject.bramble.test.TestUtils.getLocalAuthor;
|
||||
import static org.briarproject.bramble.test.TestUtils.getMessage;
|
||||
@@ -59,6 +62,19 @@ public class FeedManagerImplTest extends BrambleMockTestCase {
|
||||
private final Clock clock = context.mock(Clock.class);
|
||||
private final Dns noDnsLookups = context.mock(Dns.class);
|
||||
|
||||
private final OkHttpClient client = new OkHttpClient.Builder()
|
||||
.socketFactory(SocketFactory.getDefault())
|
||||
.dns(noDnsLookups)
|
||||
.connectTimeout(60_000, MILLISECONDS)
|
||||
.build();
|
||||
private final WeakSingletonProvider<OkHttpClient> httpClientProvider =
|
||||
new WeakSingletonProvider<OkHttpClient>() {
|
||||
@Override
|
||||
@Nonnull
|
||||
OkHttpClient createInstance() {
|
||||
return client;
|
||||
}
|
||||
};
|
||||
private final Group localGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
|
||||
private final GroupId localGroupId = localGroup.getId();
|
||||
private final Group blogGroup =
|
||||
@@ -73,7 +89,7 @@ public class FeedManagerImplTest extends BrambleMockTestCase {
|
||||
private final FeedManagerImpl feedManager =
|
||||
new FeedManagerImpl(scheduler, ioExecutor, db, contactGroupFactory,
|
||||
clientHelper, blogManager, blogPostFactory, feedFactory,
|
||||
SocketFactory.getDefault(), clock, noDnsLookups);
|
||||
httpClientProvider, clock);
|
||||
|
||||
@Test
|
||||
public void testFetchFeedsReturnsEarlyIfTorIsNotActive() {
|
||||
|
||||
@@ -7,8 +7,8 @@ import static java.util.Collections.list
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'idea'
|
||||
id 'org.jetbrains.kotlin.jvm' version '1.6.0'
|
||||
id 'org.jetbrains.kotlin.kapt' version '1.6.0'
|
||||
id 'org.jetbrains.kotlin.jvm' version '1.4.32'
|
||||
id 'org.jetbrains.kotlin.kapt' version '1.4.32'
|
||||
id 'witness'
|
||||
}
|
||||
apply from: 'witness.gradle'
|
||||
@@ -20,7 +20,7 @@ dependencies {
|
||||
implementation project(path: ':briar-core', configuration: 'default')
|
||||
implementation project(path: ':bramble-java', configuration: 'default')
|
||||
|
||||
implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.0'
|
||||
implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.32'
|
||||
implementation 'io.javalin:javalin:3.5.0'
|
||||
implementation 'org.slf4j:slf4j-simple:1.7.30'
|
||||
implementation 'com.fasterxml.jackson.core:jackson-databind:2.12.2'
|
||||
@@ -36,7 +36,7 @@ dependencies {
|
||||
def junitVersion = '5.5.2'
|
||||
testImplementation "org.junit.jupiter:junit-jupiter-api:$junitVersion"
|
||||
testImplementation "org.junit.jupiter:junit-jupiter-params:$junitVersion"
|
||||
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junitVersion"
|
||||
testRuntime "org.junit.jupiter:junit-jupiter-engine:$junitVersion"
|
||||
testImplementation 'io.mockk:mockk:1.10.4'
|
||||
testImplementation 'org.skyscreamer:jsonassert:1.5.0'
|
||||
testImplementation 'khttp:khttp:0.1.0'
|
||||
@@ -126,8 +126,4 @@ test {
|
||||
testLogging {
|
||||
events "passed", "skipped", "failed"
|
||||
}
|
||||
// for problems in ContactControllerIntegrationTest
|
||||
jvmArgs = [
|
||||
'--add-opens', 'java.base/java.net=ALL-UNNAMED',
|
||||
]
|
||||
}
|
||||
|
||||
@@ -107,5 +107,6 @@ internal class HeadlessModule(private val appDir: File) {
|
||||
override fun shouldEnableImageAttachments() = false
|
||||
override fun shouldEnableProfilePictures() = false
|
||||
override fun shouldEnableDisappearingMessages() = false
|
||||
override fun shouldEnablePersistentLogs() = false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ dependencyVerification {
|
||||
'com.fasterxml.jackson.core:jackson-core:2.12.2:jackson-core-2.12.2.jar:7883331763729b72735fdd8a117f32eb7d22695babfb37cc99df8392c196efc3',
|
||||
'com.fasterxml.jackson.core:jackson-databind:2.12.2:jackson-databind-2.12.2.jar:c4002f861d8d33f3202bf8effabb53acc320c5276cc50c1bfaae73c36ce8db32',
|
||||
'com.github.ajalt:clikt:2.2.0:clikt-2.2.0.jar:beb3136d06764ec8ce0810a8fd6c8b7b49d04287d1deef3a07c016e43a458d33',
|
||||
'com.github.gundy:semver4j:0.16.4:semver4j-0.16.4-nodeps.jar:3f59eca516374ccd4fd3551625bf50f8a4b191f700508f7ce4866460a6128af0',
|
||||
'com.github.gundy:semver4j:0.16.4:semver4j-0.16.4.jar:def9b4225fa37219e18f81d01f0e52d73dca1257a38f5475be9dd58f87736510',
|
||||
'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7',
|
||||
'com.google.code.gson:gson:2.8.6:gson-2.8.6.jar:c8fb4839054d280b3033f800d1f5a97de2f028eb8ba2eb458ad287e536f3f25f',
|
||||
'com.google.dagger:dagger-compiler:2.24:dagger-compiler-2.24.jar:3c5afb955fb188da485cb2c048eff37dce0e1530b9780a0f2f7187d16d1ccc1f',
|
||||
@@ -12,18 +12,15 @@ dependencyVerification {
|
||||
'com.google.dagger:dagger-spi:2.24:dagger-spi-2.24.jar:c038445d14dbcb4054e61bf49e05009edf26fce4fdc7ec1a9db544784f68e718',
|
||||
'com.google.dagger:dagger:2.24:dagger-2.24.jar:550a6e46a6dfcdf1d764887b6090cea94f783327e50e5c73754f18facfc70b64',
|
||||
'com.google.errorprone:error_prone_annotations:2.2.0:error_prone_annotations-2.2.0.jar:6ebd22ca1b9d8ec06d41de8d64e0596981d9607b42035f9ed374f9de271a481a',
|
||||
'com.google.errorprone:error_prone_annotations:2.3.4:error_prone_annotations-2.3.4.jar:baf7d6ea97ce606c53e11b6854ba5f2ce7ef5c24dddf0afa18d1260bd25b002c',
|
||||
'com.google.errorprone:javac-shaded:9-dev-r4023-3:javac-shaded-9-dev-r4023-3.jar:65bfccf60986c47fbc17c9ebab0be626afc41741e0a6ec7109e0768817a36f30',
|
||||
'com.google.googlejavaformat:google-java-format:1.5:google-java-format-1.5.jar:aa19ad7850fb85178aa22f2fddb163b84d6ce4d0035872f30d4408195ca1144e',
|
||||
'com.google.guava:failureaccess:1.0.1:failureaccess-1.0.1.jar:a171ee4c734dd2da837e4b16be9df4661afab72a41adaf31eb84dfdaf936ca26',
|
||||
'com.google.guava:guava:27.1-jre:guava-27.1-jre.jar:4a5aa70cc968a4d137e599ad37553e5cfeed2265e8c193476d7119036c536fe7',
|
||||
'com.google.guava:guava:29.0-jre:guava-29.0-jre.jar:b22c5fb66d61e7b9522531d04b2f915b5158e80aa0b40ee7282c8bfb07b0da25',
|
||||
'com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava:listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar:b372a037d4230aa57fbeffdef30fd6123f9c0c2db85d0aced00c91b974f33f99',
|
||||
'com.google.j2objc:j2objc-annotations:1.1:j2objc-annotations-1.1.jar:2994a7eb78f2710bd3d3bfb639b2c94e219cedac0d4d084d516e78c16dddecf6',
|
||||
'com.google.j2objc:j2objc-annotations:1.3:j2objc-annotations-1.3.jar:21af30c92267bd6122c0e0b4d20cccb6641a37eaf956c6540ec471d584e64a7b',
|
||||
'com.squareup:javapoet:1.11.1:javapoet-1.11.1.jar:9cbf2107be499ec6e95afd36b58e3ca122a24166cdd375732e51267d64058e90',
|
||||
'com.vaadin.external.google:android-json:0.0.20131108.vaadin1:android-json-0.0.20131108.vaadin1.jar:dfb7bae2f404cfe0b72b4d23944698cb716b7665171812a0a4d0f5926c0fac79',
|
||||
'de.undercouch:gradle-download-task:4.1.1:gradle-download-task-4.1.1.jar:eb08b570e408d4646705e70a949614d439ea2b11455f1652ab0330de8954dab9',
|
||||
'de.undercouch:gradle-download-task:4.0.2:gradle-download-task-4.0.2.jar:952cbfcc5f21beeccb5925cc5ba648af09839258441dd44d087d64a57d34e87a',
|
||||
'io.javalin:javalin:3.5.0:javalin-3.5.0.jar:6618f99ad4c241eefcaf3a02c85adc52ec346c9710e8eb5a3f1a916e3d7acec4',
|
||||
'io.mockk:mockk-agent-api:1.10.4:mockk-agent-api-1.10.4.jar:8deb59189b48d5870a746f954ca681424040544812c7ae295f3bef87a9499cfe',
|
||||
'io.mockk:mockk-agent-common:1.10.4:mockk-agent-common-1.10.4.jar:13b81a3297a3c15ed9f62b838aaede20347018f07c30cad2ca74a4dd99786f8f',
|
||||
@@ -38,11 +35,10 @@ dependencyVerification {
|
||||
'khttp:khttp:0.1.0:khttp-0.1.0.jar:48ab3bd22e461f2c2e74e3446d8f9568e24aab157f61fdc85ded6c0bfbe9a926',
|
||||
'net.bytebuddy:byte-buddy-agent:1.10.14:byte-buddy-agent-1.10.14.jar:30272167eceb1cb68fa84730a12d1abfd1daed6ae0c19fdefee47a9a9a0cfd33',
|
||||
'net.bytebuddy:byte-buddy:1.10.14:byte-buddy-1.10.14.jar:0e6b935bfcb3e451d525956acad53ec86ff916d714abdbd32b3d2039771896f8',
|
||||
'net.java.dev.jna:jna:5.6.0:jna-5.6.0.jar:5557e235a8aa2f9766d5dc609d67948f2a8832c2d796cea9ef1d6cbe0b3b7eaf',
|
||||
'net.ltgt.gradle.incap:incap:0.2:incap-0.2.jar:b625b9806b0f1e4bc7a2e3457119488de3cd57ea20feedd513db070a573a4ffd',
|
||||
'org.antlr:antlr4-runtime:4.5.2-1:antlr4-runtime-4.5.2-1.jar:e831413004bceed7d915c3a175927b1daabc4974b7b8a6f87bbce886d3550398',
|
||||
'org.apiguardian:apiguardian-api:1.1.0:apiguardian-api-1.1.0.jar:a9aae9ff8ae3e17a2a18f79175e82b16267c246fbbd3ca9dfbbb290b08dcfdd4',
|
||||
'org.checkerframework:checker-compat-qual:2.5.3:checker-compat-qual-2.5.3.jar:d76b9afea61c7c082908023f0cbc1427fab9abd2df915c8b8a3e7a509bccbc6d',
|
||||
'org.checkerframework:checker-qual:2.11.1:checker-qual-2.11.1.jar:015224a4b1dc6de6da053273d4da7d39cfea20e63038169fc45ac0d1dc9c5938',
|
||||
'org.checkerframework:checker-qual:2.5.2:checker-qual-2.5.2.jar:64b02691c8b9d4e7700f8ee2e742dce7ea2c6e81e662b7522c9ee3bf568c040a',
|
||||
'org.codehaus.mojo:animal-sniffer-annotations:1.17:animal-sniffer-annotations-1.17.jar:92654f493ecfec52082e76354f0ebf87648dc3d5cec2e3c3cdb947c016747a53',
|
||||
'org.eclipse.jetty.websocket:websocket-api:9.4.20.v20190813:websocket-api-9.4.20.v20190813.jar:779a29060cc17bdeeeba147efc884ebff972cfff93dad2d37b11c93f95d4f67b',
|
||||
@@ -60,35 +56,31 @@ dependencyVerification {
|
||||
'org.eclipse.jetty:jetty-webapp:9.4.20.v20190813:jetty-webapp-9.4.20.v20190813.jar:59d9b5f238acb14eac3bf90f755eeabd9fc16c630217d0e7e01b99a38194036c',
|
||||
'org.eclipse.jetty:jetty-xml:9.4.20.v20190813:jetty-xml-9.4.20.v20190813.jar:f4411ad9998e4cc202c849bb9b9e93aa2aa761b89a27cc746ca025849d659fd0',
|
||||
'org.jetbrains.intellij.deps:trove4j:1.0.20181211:trove4j-1.0.20181211.jar:affb7c85a3c87bdcf69ff1dbb84de11f63dc931293934bc08cd7ab18de083601',
|
||||
'org.jetbrains.kotlin:kotlin-android-extensions:1.6.0:kotlin-android-extensions-1.6.0.jar:4cfaf3f0120517bf1d6d8d3bafcfae1e898c67d3f7c8b7687081bfb8193c7ed4',
|
||||
'org.jetbrains.kotlin:kotlin-annotation-processing-gradle:1.6.0:kotlin-annotation-processing-gradle-1.6.0.jar:8673fffffb00ab16033d90f6c7fec10b5d310ca40fbe43519ab4118bfc84179c',
|
||||
'org.jetbrains.kotlin:kotlin-build-common:1.6.0:kotlin-build-common-1.6.0.jar:e24a7b706ba45618b4ee43966b62501136d0060b032f8e9f18d183da05b1bf43',
|
||||
'org.jetbrains.kotlin:kotlin-compiler-embeddable:1.6.0:kotlin-compiler-embeddable-1.6.0.jar:0366843cd2defdd583c6b16b10bc32b85f28c5bf9510f10e44c886f5bd24c388',
|
||||
'org.jetbrains.kotlin:kotlin-compiler-runner:1.6.0:kotlin-compiler-runner-1.6.0.jar:6af7520af84a442c6abf1bd7285ba71bac7365660b2d08fd04c84ec353633d49',
|
||||
'org.jetbrains.kotlin:kotlin-daemon-client:1.6.0:kotlin-daemon-client-1.6.0.jar:6c1b2fde3315879d0c977e5f2515397e7829bd0adf8921e4c5fdbad832ebc2a1',
|
||||
'org.jetbrains.kotlin:kotlin-daemon-embeddable:1.6.0:kotlin-daemon-embeddable-1.6.0.jar:20d08706aa17762fe5ab03a916d62c9b7ee211d20844a8aabe0db83d9d90284a',
|
||||
'org.jetbrains.kotlin:kotlin-gradle-plugin-api:1.6.0:kotlin-gradle-plugin-api-1.6.0.jar:c8692c338826c48425bfc87fab69cb79fe2d4f5f81ff058c929418bf0da97d15',
|
||||
'org.jetbrains.kotlin:kotlin-gradle-plugin-model:1.6.0:kotlin-gradle-plugin-model-1.6.0.jar:9def4d1cf0d9577a511a2aa4f7f310eccc24243b05c02a717a5131c4c8f9965e',
|
||||
'org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.0:kotlin-gradle-plugin-1.6.0.jar:b7037ad66eb0c9b59badcaa238d1c5426715b797aa7709347307b75c1adac16a',
|
||||
'org.jetbrains.kotlin:kotlin-klib-commonizer-api:1.6.0:kotlin-klib-commonizer-api-1.6.0.jar:fc6ab39e90d82226d9b8bcce01876164e5bafd1ac0a203f3f03078cb1b48301f',
|
||||
'org.jetbrains.kotlin:kotlin-klib-commonizer-embeddable:1.6.0:kotlin-klib-commonizer-embeddable-1.6.0.jar:c7c8116263d285fe538e7c196841e235aa8f7b9d9f6ac3613f88de53b261d8c3',
|
||||
'org.jetbrains.kotlin:kotlin-native-utils:1.6.0:kotlin-native-utils-1.6.0.jar:a0b0fd7deaf4fe10b0671355258bc63759e3d24a7115ceff87bc741bfa7ce9ec',
|
||||
'org.jetbrains.kotlin:kotlin-project-model:1.6.0:kotlin-project-model-1.6.0.jar:38135d3f995fc505d604aed2e82ac5ba858b2a6a3e476899c3b8be47fd222b68',
|
||||
'org.jetbrains.kotlin:kotlin-android-extensions:1.4.32:kotlin-android-extensions-1.4.32.jar:be4dcefa4274c9c93703fec984e53d19cac9b9c95e3567247aa0257267266529',
|
||||
'org.jetbrains.kotlin:kotlin-annotation-processing-gradle:1.4.32:kotlin-annotation-processing-gradle-1.4.32.jar:0ef86e325c44cb7476b862e3319226cb85852b2dc9f37a545e856b617ded1691',
|
||||
'org.jetbrains.kotlin:kotlin-build-common:1.4.32:kotlin-build-common-1.4.32.jar:d8c1fab9ff7dfdb385fc0789da5f2574114926897060fcf7cc6d93207ae88ee4',
|
||||
'org.jetbrains.kotlin:kotlin-compiler-embeddable:1.4.32:kotlin-compiler-embeddable-1.4.32.jar:083d80ea6262faac293d248c32bf89e062a4e44d657ea6a095c8066e31791e5e',
|
||||
'org.jetbrains.kotlin:kotlin-compiler-runner:1.4.32:kotlin-compiler-runner-1.4.32.jar:9f668c4033b8c28eed076f39ad93749911d01671e887369a86fc2a9ed5cb2bc3',
|
||||
'org.jetbrains.kotlin:kotlin-daemon-client:1.4.32:kotlin-daemon-client-1.4.32.jar:4c77d463ba41fb43f9e8a7868fc99712431e8f6b3b8df24aa7df3e5778863a6c',
|
||||
'org.jetbrains.kotlin:kotlin-daemon-embeddable:1.4.32:kotlin-daemon-embeddable-1.4.32.jar:0c52722dfb15d6c79f77e1c1c55caf93d0a480f9e1ee76da751cf0cc1e4b6d19',
|
||||
'org.jetbrains.kotlin:kotlin-gradle-plugin-api:1.4.32:kotlin-gradle-plugin-api-1.4.32.jar:d0655390868ebade8b30a36607f30b0031c898f7f433d3ea5ff8426a9afa056b',
|
||||
'org.jetbrains.kotlin:kotlin-gradle-plugin-model:1.4.32:kotlin-gradle-plugin-model-1.4.32.jar:628b5abe97e47fa8d1bf38e5e58be600f720084a871e8f77d9713a895d0e3b40',
|
||||
'org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.32:kotlin-gradle-plugin-1.4.32.jar:369d6c3636d74e1328a12a689adbf76cc16bcc11cf9d594dda2e4b0952068ad8',
|
||||
'org.jetbrains.kotlin:kotlin-klib-commonizer-embeddable:1.4.32:kotlin-klib-commonizer-embeddable-1.4.32.jar:6e38c9c7dc14c2913a67f1690ccb1efb9bb2d1fe211a5628c9470195cc6e4edf',
|
||||
'org.jetbrains.kotlin:kotlin-reflect:1.4.20:kotlin-reflect-1.4.20.jar:3b7c82def79fb96c4579d40a47e37dec872f9f8209ee0da3ce828c39dba612e1',
|
||||
'org.jetbrains.kotlin:kotlin-reflect:1.6.0:kotlin-reflect-1.6.0.jar:c6161884209221db7f5ddb031bb480a3c46bb90d5b65d7cc0167b149aaa9c494',
|
||||
'org.jetbrains.kotlin:kotlin-script-runtime:1.6.0:kotlin-script-runtime-1.6.0.jar:ddca0f765c416e77a4d8816f3d2df6eda953f61af811737846a22033225a0e57',
|
||||
'org.jetbrains.kotlin:kotlin-scripting-common:1.6.0:kotlin-scripting-common-1.6.0.jar:16699b070afc4422300c9ed66e81e98b65f7c691faa852b8d44195e509dd6d22',
|
||||
'org.jetbrains.kotlin:kotlin-scripting-compiler-embeddable:1.6.0:kotlin-scripting-compiler-embeddable-1.6.0.jar:2cd1c6f6af69c16b7934d7d8d67c183349b434f450482d7229a9607136fa0447',
|
||||
'org.jetbrains.kotlin:kotlin-scripting-compiler-impl-embeddable:1.6.0:kotlin-scripting-compiler-impl-embeddable-1.6.0.jar:e4bd48906746e4cd19e016445599dca2683a994da06ac38cad383aac48338da8',
|
||||
'org.jetbrains.kotlin:kotlin-scripting-jvm:1.6.0:kotlin-scripting-jvm-1.6.0.jar:5f6a7ea274cb6c6c4372094a3572df2a392aa5389f1553b824873d62d6003652',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib-common:1.6.0:kotlin-stdlib-common-1.6.0.jar:644a7257c23b51a1fd5068960e40922e3e52c219f11ece3e040a3abc74823f22',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.6.0:kotlin-stdlib-jdk7-1.6.0.jar:870d35fd266b2daf64c1080fe51824d3c368f7995384a8d7c5fc2fdc40eb7b3a',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.0:kotlin-stdlib-jdk8-1.6.0.jar:ab0547c495953214a5f2b28150014f4e02133678d52b77d76375ea235e443dbd',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib:1.6.0:kotlin-stdlib-1.6.0.jar:115daea30b0d484afcf2360237b9d9537f48a4a2f03f3cc2a16577dfc6e90342',
|
||||
'org.jetbrains.kotlin:kotlin-tooling-metadata:1.6.0:kotlin-tooling-metadata-1.6.0.jar:5f79b547f80ceba482d6f71844b23445e109f685eb74481044678cd75c28ad77',
|
||||
'org.jetbrains.kotlin:kotlin-util-io:1.6.0:kotlin-util-io-1.6.0.jar:adb7874ef77128ab3b0953ff49ec801239c837fe7131c3147cef9db8bbdf5739',
|
||||
'org.jetbrains.kotlin:kotlin-util-klib:1.6.0:kotlin-util-klib-1.6.0.jar:a9631c0b7823c69b8d33502a2863e7574308d9f9c12e3f28f4b5bbf6c2a41f74',
|
||||
'org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.5.0:kotlinx-coroutines-core-jvm-1.5.0.jar:78d6cc7135f84d692ff3752fcfd1fa1bbe0940d7df70652e4f1eaeec0c78afbb',
|
||||
'org.jetbrains.kotlin:kotlin-reflect:1.4.32:kotlin-reflect-1.4.32.jar:dbf19e9cdaa9c3c170f3f6f6ce3922f38dfc1d7fa1cab5b7c23a19da8b5eec5b',
|
||||
'org.jetbrains.kotlin:kotlin-script-runtime:1.4.32:kotlin-script-runtime-1.4.32.jar:4496e90565b6cc312213acd65fe8ad6d149264ff12d2f1f6b6ba4122afffbbfe',
|
||||
'org.jetbrains.kotlin:kotlin-scripting-common:1.4.32:kotlin-scripting-common-1.4.32.jar:58705f21ba97f2d2e8b818d3c8167252e2b210a610e5678b008bc779f3745112',
|
||||
'org.jetbrains.kotlin:kotlin-scripting-compiler-embeddable:1.4.32:kotlin-scripting-compiler-embeddable-1.4.32.jar:cc4db11fd2ca73250a30e42d6783973aae13b1e3e71520273d4c1354262ee384',
|
||||
'org.jetbrains.kotlin:kotlin-scripting-compiler-impl-embeddable:1.4.32:kotlin-scripting-compiler-impl-embeddable-1.4.32.jar:66940ccb8c5e182d7d2ac47f0dfeccc224c4deea077361cf3935c4e0460d70ad',
|
||||
'org.jetbrains.kotlin:kotlin-scripting-jvm:1.4.32:kotlin-scripting-jvm-1.4.32.jar:d2ccd108b7d68bf38657487114bd54c95deae375ee959f9e7805c59eb037fb98',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib-common:1.4.32:kotlin-stdlib-common-1.4.32.jar:e1ff6f55ee9e7591dcc633f7757bac25a7edb1cc7f738b37ec652f10f66a4145',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.4.32:kotlin-stdlib-jdk7-1.4.32.jar:5f801e75ca27d8791c14b07943c608da27620d910a8093022af57f543d5d98b6',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.32:kotlin-stdlib-jdk8-1.4.32.jar:adc43e54757b106e0cd7b3b7aa257dff471b61efdabe067fc02b2f57e2396262',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib:1.4.32:kotlin-stdlib-1.4.32.jar:13e9fd3e69dc7230ce0fc873a92a4e5d521d179bcf1bef75a6705baac3bfecba',
|
||||
'org.jetbrains.kotlin:kotlin-util-io:1.4.32:kotlin-util-io-1.4.32.jar:d8b33d8840ff755e686d41b0fa3a27272849a2ac8242554606e8d66462bc607f',
|
||||
'org.jetbrains.kotlin:kotlin-util-klib:1.4.32:kotlin-util-klib-1.4.32.jar:4a80f7a521f70a87798e74416b596336c76d8306594172a4cf142c16e1720081',
|
||||
'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.8:kotlinx-coroutines-core-1.3.8.jar:f8c8b7485d4a575e38e5e94945539d1d4eccd3228a199e1a9aa094e8c26174ee',
|
||||
'org.jetbrains:annotations:13.0:annotations-13.0.jar:ace2a10dc8e2d5fd34925ecac03e4988b2c0f851650c94b8cef49ba1bd111478',
|
||||
'org.json:json:20150729:json-20150729.jar:38c21b9c3d6d24919cd15d027d20afab0a019ac9205f7ed9083b32bdd42a2353',
|
||||
'org.junit.jupiter:junit-jupiter-api:5.5.2:junit-jupiter-api-5.5.2.jar:249a2fdbd3931987c0298d00ca08ed248496e0fc11e0463c08c4f82e0cc79b1c',
|
||||
|
||||
Reference in New Issue
Block a user