diff --git a/briar-android/src/main/AndroidManifest.xml b/briar-android/src/main/AndroidManifest.xml
index 19b55dca6..366cb24e8 100644
--- a/briar-android/src/main/AndroidManifest.xml
+++ b/briar-android/src/main/AndroidManifest.xml
@@ -12,7 +12,6 @@
-
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/BriarApplication.java b/briar-android/src/main/java/org/briarproject/briar/android/BriarApplication.java
index bf78cecfa..e537bd86d 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/BriarApplication.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/BriarApplication.java
@@ -1,10 +1,15 @@
package org.briarproject.briar.android;
+import java.util.Collection;
+import java.util.logging.LogRecord;
+
/**
* This exists so that the Application object will not necessarily be cast
* directly to the Briar application object.
*/
public interface BriarApplication {
+ Collection getRecentLogRecords();
+
AndroidComponent getApplicationComponent();
}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/BriarApplicationImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/BriarApplicationImpl.java
index 19ea60a89..97e19062a 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/BriarApplicationImpl.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/BriarApplicationImpl.java
@@ -12,12 +12,17 @@ import org.acra.annotation.ReportsCrashes;
import org.briarproject.bramble.BrambleCoreModule;
import org.briarproject.briar.BriarCoreModule;
import org.briarproject.briar.R;
+import org.briarproject.briar.android.logging.CachingLogHandler;
import org.briarproject.briar.android.reporting.BriarReportPrimer;
import org.briarproject.briar.android.reporting.BriarReportSenderFactory;
import org.briarproject.briar.android.reporting.DevReportActivity;
+import java.util.Collection;
+import java.util.logging.Handler;
+import java.util.logging.LogRecord;
import java.util.logging.Logger;
+import static java.util.logging.Level.INFO;
import static org.acra.ReportField.ANDROID_VERSION;
import static org.acra.ReportField.APP_VERSION_CODE;
import static org.acra.ReportField.APP_VERSION_NAME;
@@ -28,7 +33,6 @@ import static org.acra.ReportField.CUSTOM_DATA;
import static org.acra.ReportField.DEVICE_FEATURES;
import static org.acra.ReportField.DISPLAY;
import static org.acra.ReportField.INITIAL_CONFIGURATION;
-import static org.acra.ReportField.LOGCAT;
import static org.acra.ReportField.PACKAGE_NAME;
import static org.acra.ReportField.PHONE_MODEL;
import static org.acra.ReportField.PRODUCT;
@@ -36,7 +40,7 @@ import static org.acra.ReportField.REPORT_ID;
import static org.acra.ReportField.STACK_TRACE;
import static org.acra.ReportField.USER_APP_START_DATE;
import static org.acra.ReportField.USER_CRASH_DATE;
-import static org.briarproject.briar.android.TestingConstants.DEFAULT_LOG_LEVEL;
+import static org.briarproject.briar.android.TestingConstants.IS_BETA_BUILD;
import static org.briarproject.briar.android.TestingConstants.IS_DEBUG_BUILD;
@ReportsCrashes(
@@ -56,8 +60,7 @@ import static org.briarproject.briar.android.TestingConstants.IS_DEBUG_BUILD;
STACK_TRACE,
INITIAL_CONFIGURATION, CRASH_CONFIGURATION,
DISPLAY, DEVICE_FEATURES,
- USER_APP_START_DATE, USER_CRASH_DATE,
- LOGCAT
+ USER_APP_START_DATE, USER_CRASH_DATE
}
)
public class BriarApplicationImpl extends Application
@@ -66,6 +69,8 @@ public class BriarApplicationImpl extends Application
private static final Logger LOG =
Logger.getLogger(BriarApplicationImpl.class.getName());
+ private final CachingLogHandler logHandler = new CachingLogHandler();
+
private AndroidComponent applicationComponent;
@Override
@@ -79,7 +84,17 @@ public class BriarApplicationImpl extends Application
super.onCreate();
if (IS_DEBUG_BUILD) enableStrictMode();
- Logger.getLogger("").setLevel(DEFAULT_LOG_LEVEL);
+
+ Logger rootLogger = Logger.getLogger("");
+ if (!IS_DEBUG_BUILD && !IS_BETA_BUILD) {
+ // Remove default log handlers so system log is not used
+ for (Handler handler : rootLogger.getHandlers()) {
+ rootLogger.removeHandler(handler);
+ }
+ }
+ rootLogger.addHandler(logHandler);
+ rootLogger.setLevel(INFO);
+
LOG.info("Created");
applicationComponent = DaggerAndroidComponent.builder()
@@ -104,6 +119,11 @@ public class BriarApplicationImpl extends Application
StrictMode.setVmPolicy(vmPolicy.build());
}
+ @Override
+ public Collection getRecentLogRecords() {
+ return logHandler.getRecentLogRecords();
+ }
+
@Override
public AndroidComponent getApplicationComponent() {
return applicationComponent;
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/TestingConstants.java b/briar-android/src/main/java/org/briarproject/briar/android/TestingConstants.java
index 96f1b5cf2..e3b970dc9 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/TestingConstants.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/TestingConstants.java
@@ -2,11 +2,6 @@ package org.briarproject.briar.android;
import org.briarproject.briar.BuildConfig;
-import java.util.logging.Level;
-
-import static java.util.logging.Level.INFO;
-import static java.util.logging.Level.OFF;
-
public interface TestingConstants {
/**
@@ -20,12 +15,6 @@ public interface TestingConstants {
*/
boolean IS_BETA_BUILD = false;
- /**
- * Default log level. Disable logging for final release builds.
- */
- @SuppressWarnings("ConstantConditions")
- Level DEFAULT_LOG_LEVEL = IS_DEBUG_BUILD || IS_BETA_BUILD ? INFO : OFF;
-
/**
* Whether to prevent screenshots from being taken. Setting this to true
* prevents Recent Apps from storing screenshots of private information.
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/logging/BriefLogFormatter.java b/briar-android/src/main/java/org/briarproject/briar/android/logging/BriefLogFormatter.java
new file mode 100644
index 000000000..681162aec
--- /dev/null
+++ b/briar-android/src/main/java/org/briarproject/briar/android/logging/BriefLogFormatter.java
@@ -0,0 +1,47 @@
+package org.briarproject.briar.android.logging;
+
+import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.TimeZone;
+import java.util.logging.Formatter;
+import java.util.logging.LogRecord;
+
+import javax.annotation.concurrent.ThreadSafe;
+
+import static java.util.Locale.US;
+
+@ThreadSafe
+@NotNullByDefault
+public class BriefLogFormatter extends Formatter {
+
+ private final Object lock = new Object();
+ private final DateFormat dateFormat; // Locking: lock
+ private final Date date; // Locking: lock
+
+ public BriefLogFormatter() {
+ synchronized (lock) {
+ dateFormat = new SimpleDateFormat("MM-dd HH:mm:ss.SSS ", US);
+ dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ date = new Date();
+ }
+ }
+
+ @Override
+ public String format(LogRecord record) {
+ String dateString;
+ synchronized (lock) {
+ date.setTime(record.getMillis());
+ dateString = dateFormat.format(date);
+ }
+ StringBuilder sb = new StringBuilder(dateString);
+ sb.append(record.getLevel().getName().charAt(0)).append('/');
+ String tag = record.getLoggerName();
+ tag = tag.substring(tag.lastIndexOf('.') + 1);
+ sb.append(tag).append(": ");
+ sb.append(record.getMessage());
+ return sb.toString();
+ }
+}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/logging/CachingLogHandler.java b/briar-android/src/main/java/org/briarproject/briar/android/logging/CachingLogHandler.java
new file mode 100644
index 000000000..baa51e3f6
--- /dev/null
+++ b/briar-android/src/main/java/org/briarproject/briar/android/logging/CachingLogHandler.java
@@ -0,0 +1,48 @@
+package org.briarproject.briar.android.logging;
+
+import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.Queue;
+import java.util.logging.Handler;
+import java.util.logging.LogRecord;
+
+import javax.annotation.concurrent.ThreadSafe;
+
+@ThreadSafe
+@NotNullByDefault
+public class CachingLogHandler extends Handler {
+
+ private static final int MAX_RECENT_RECORDS = 100;
+
+ private final Object lock = new Object();
+ // Locking: lock
+ private final Queue recent = new LinkedList<>();
+
+ @Override
+ public void publish(LogRecord record) {
+ synchronized (lock) {
+ recent.add(record);
+ if (recent.size() > MAX_RECENT_RECORDS) recent.poll();
+ }
+ }
+
+ @Override
+ public void flush() {
+ }
+
+ @Override
+ public void close() {
+ synchronized (lock) {
+ recent.clear();
+ }
+ }
+
+ public Collection getRecentLogRecords() {
+ synchronized (lock) {
+ return new ArrayList<>(recent);
+ }
+ }
+}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/reporting/BriarReportPrimer.java b/briar-android/src/main/java/org/briarproject/briar/android/reporting/BriarReportPrimer.java
index f74647eba..dbf673242 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/reporting/BriarReportPrimer.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/reporting/BriarReportPrimer.java
@@ -18,6 +18,8 @@ import org.acra.builder.ReportBuilder;
import org.acra.builder.ReportPrimer;
import org.briarproject.bramble.util.StringUtils;
import org.briarproject.briar.BuildConfig;
+import org.briarproject.briar.android.BriarApplication;
+import org.briarproject.briar.android.logging.BriefLogFormatter;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
@@ -28,6 +30,8 @@ import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
+import java.util.logging.Formatter;
+import java.util.logging.LogRecord;
import static android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE;
import static android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE;
@@ -69,6 +73,16 @@ public class BriarReportPrimer implements ReportPrimer {
public Map call() {
Map customData = new LinkedHashMap<>();
+ // Log
+ BriarApplication app =
+ (BriarApplication) ctx.getApplicationContext();
+ StringBuilder sb = new StringBuilder();
+ Formatter formatter = new BriefLogFormatter();
+ for (LogRecord record : app.getRecentLogRecords()) {
+ sb.append(formatter.format(record)).append('\n');
+ }
+ customData.put("Log", sb.toString());
+
// System memory
Object o = ctx.getSystemService(ACTIVITY_SERVICE);
ActivityManager am = (ActivityManager) o;
@@ -223,9 +237,10 @@ public class BriarReportPrimer implements ReportPrimer {
customData.put("Bluetooth LE status", btLeStatus);
}
- if (bt != null)
+ if (bt != null) {
customData.put("Bluetooth address",
scrubMacAddress(bt.getAddress()));
+ }
String btSettingsAddr;
try {
btSettingsAddr = Settings.Secure.getString(
diff --git a/briar-android/src/test/java/org/briarproject/briar/android/TestBriarApplication.java b/briar-android/src/test/java/org/briarproject/briar/android/TestBriarApplication.java
index bc6016caa..6f232bef0 100644
--- a/briar-android/src/test/java/org/briarproject/briar/android/TestBriarApplication.java
+++ b/briar-android/src/test/java/org/briarproject/briar/android/TestBriarApplication.java
@@ -5,8 +5,12 @@ import android.app.Application;
import org.briarproject.bramble.BrambleCoreModule;
import org.briarproject.briar.BriarCoreModule;
+import java.util.Collection;
+import java.util.logging.LogRecord;
import java.util.logging.Logger;
+import static java.util.Collections.emptyList;
+
/**
* This class only exists to avoid static initialisation of ACRA
*/
@@ -34,6 +38,11 @@ public class TestBriarApplication extends Application
AndroidEagerSingletons.initEagerSingletons(applicationComponent);
}
+ @Override
+ public Collection getRecentLogRecords() {
+ return emptyList();
+ }
+
@Override
public AndroidComponent getApplicationComponent() {
return applicationComponent;