diff --git a/app/build.gradle b/app/build.gradle index 89327b29d..f81c02db2 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -153,7 +153,7 @@ dependencies { testImplementation 'org.robolectric:shadows-multidex:4.4' } -def canonicalVersionCode = 222 +def canonicalVersionCode = 223 def canonicalVersionName = "1.11.10" def postFixSize = 10 diff --git a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java index 363343696..1643f63bb 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java +++ b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java @@ -15,6 +15,9 @@ */ package org.thoughtcrime.securesms; +import static nl.komponents.kovenant.android.KovenantAndroid.startKovenant; +import static nl.komponents.kovenant.android.KovenantAndroid.stopKovenant; + import android.app.Application; import android.content.Context; import android.content.Intent; @@ -48,24 +51,23 @@ import org.signal.aesgcmprovider.AesGcmProvider; import org.thoughtcrime.securesms.components.TypingStatusSender; import org.thoughtcrime.securesms.crypto.KeyPairUtilities; import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.database.LokiAPIDatabase; import org.thoughtcrime.securesms.dependencies.InjectableType; import org.thoughtcrime.securesms.dependencies.SignalCommunicationModule; +import org.thoughtcrime.securesms.groups.OpenGroupManager; +import org.thoughtcrime.securesms.home.HomeActivity; import org.thoughtcrime.securesms.jobmanager.DependencyInjector; import org.thoughtcrime.securesms.jobmanager.JobManager; import org.thoughtcrime.securesms.jobmanager.impl.JsonDataSerializer; import org.thoughtcrime.securesms.jobs.FastJobStorage; import org.thoughtcrime.securesms.jobs.JobManagerFactories; import org.thoughtcrime.securesms.logging.AndroidLogger; +import org.thoughtcrime.securesms.logging.PersistentLogger; import org.thoughtcrime.securesms.logging.UncaughtExceptionLogger; -import org.thoughtcrime.securesms.home.HomeActivity; import org.thoughtcrime.securesms.notifications.BackgroundPollWorker; -import org.thoughtcrime.securesms.notifications.LokiPushNotificationManager; -import org.thoughtcrime.securesms.groups.OpenGroupManager; -import org.thoughtcrime.securesms.database.LokiAPIDatabase; -import org.thoughtcrime.securesms.util.Broadcaster; -import org.thoughtcrime.securesms.notifications.FcmUtils; -import org.thoughtcrime.securesms.util.UiModeUtilities; import org.thoughtcrime.securesms.notifications.DefaultMessageNotifier; +import org.thoughtcrime.securesms.notifications.FcmUtils; +import org.thoughtcrime.securesms.notifications.LokiPushNotificationManager; import org.thoughtcrime.securesms.notifications.NotificationChannels; import org.thoughtcrime.securesms.notifications.OptimizedMessageNotifier; import org.thoughtcrime.securesms.providers.BlobProvider; @@ -75,6 +77,8 @@ import org.thoughtcrime.securesms.service.UpdateApkRefreshListener; import org.thoughtcrime.securesms.sskenvironment.ProfileManager; import org.thoughtcrime.securesms.sskenvironment.ReadReceiptManager; import org.thoughtcrime.securesms.sskenvironment.TypingStatusRepository; +import org.thoughtcrime.securesms.util.Broadcaster; +import org.thoughtcrime.securesms.util.UiModeUtilities; import org.thoughtcrime.securesms.util.dynamiclanguage.LocaleParseHelper; import org.webrtc.PeerConnectionFactory; import org.webrtc.PeerConnectionFactory.InitializationOptions; @@ -93,9 +97,6 @@ import kotlin.Unit; import kotlinx.coroutines.Job; import network.loki.messenger.BuildConfig; -import static nl.komponents.kovenant.android.KovenantAndroid.startKovenant; -import static nl.komponents.kovenant.android.KovenantAndroid.stopKovenant; - /** * Will be called once when the TextSecure process is created. *
@@ -276,7 +277,7 @@ public class ApplicationContext extends Application implements DependencyInjecto
}
private void initializeLogging() {
- Log.initialize(new AndroidLogger());
+ Log.initialize(new AndroidLogger(), new PersistentLogger(this));
}
private void initializeCrashHandling() {
diff --git a/app/src/main/java/org/thoughtcrime/securesms/logging/PersistentLogger.java b/app/src/main/java/org/thoughtcrime/securesms/logging/PersistentLogger.java
new file mode 100644
index 000000000..ab0b42e21
--- /dev/null
+++ b/app/src/main/java/org/thoughtcrime/securesms/logging/PersistentLogger.java
@@ -0,0 +1,245 @@
+package org.thoughtcrime.securesms.logging;
+
+import android.content.Context;
+
+import androidx.annotation.AnyThread;
+import androidx.annotation.WorkerThread;
+
+import org.session.libsignal.utilities.ListenableFuture;
+import org.session.libsignal.utilities.Log;
+import org.session.libsignal.utilities.NoExternalStorageException;
+import org.session.libsignal.utilities.SettableFuture;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+
+public class PersistentLogger extends Log.Logger {
+
+ private static final String TAG = PersistentLogger.class.getSimpleName();
+
+ private static final String LOG_V = "V";
+ private static final String LOG_D = "D";
+ private static final String LOG_I = "I";
+ private static final String LOG_W = "W";
+ private static final String LOG_E = "E";
+ private static final String LOG_WTF = "A";
+
+ private static final String LOG_DIRECTORY = "log";
+ private static final String FILENAME_PREFIX = "log-";
+ private static final int MAX_LOG_FILES = 5;
+ private static final int MAX_LOG_SIZE = 300 * 1024;
+ private static final int MAX_LOG_EXPORT = 10_000;
+ private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS zzz");
+
+ private final Context context;
+ private final Executor executor;
+ private final byte[] secret;
+
+ private LogFile.Writer writer;
+
+ public PersistentLogger(Context context) {
+ this.context = context.getApplicationContext();
+ this.secret = LogSecretProvider.getOrCreateAttachmentSecret(context);
+ this.executor = Executors.newSingleThreadExecutor(r -> {
+ Thread thread = new Thread(r, "PersistentLogger");
+ thread.setPriority(Thread.MIN_PRIORITY);
+ return thread;
+ });
+
+ executor.execute(this::initializeWriter);
+ }
+
+ @Override
+ public void v(String tag, String message, Throwable t) {
+ write(LOG_V, tag, message, t);
+ }
+
+ @Override
+ public void d(String tag, String message, Throwable t) {
+ write(LOG_D, tag, message, t);
+ }
+
+ @Override
+ public void i(String tag, String message, Throwable t) {
+ write(LOG_I, tag, message, t);
+ }
+
+ @Override
+ public void w(String tag, String message, Throwable t) {
+ write(LOG_W, tag, message, t);
+ }
+
+ @Override
+ public void e(String tag, String message, Throwable t) {
+ write(LOG_E, tag, message, t);
+ }
+
+ @Override
+ public void wtf(String tag, String message, Throwable t) {
+ write(LOG_WTF, tag, message, t);
+ }
+
+ @Override
+ public void blockUntilAllWritesFinished() {
+ CountDownLatch latch = new CountDownLatch(1);
+
+ executor.execute(latch::countDown);
+
+ try {
+ latch.await();
+ } catch (InterruptedException e) {
+ android.util.Log.w(TAG, "Failed to wait for all writes.");
+ }
+ }
+
+ @WorkerThread
+ public ListenableFuture