diff --git a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java index 6ea6795f12..e2c82814e3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java +++ b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java @@ -56,7 +56,6 @@ 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; @@ -118,7 +117,6 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc private ReadReceiptManager readReceiptManager; private ProfileManager profileManager; private ObjectGraph objectGraph; - private PersistentLogger persistentLogger; public MessageNotifier messageNotifier = null; public Poller poller = null; public Broadcaster broadcaster = null; @@ -248,10 +246,6 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc return isAppVisible; } - public PersistentLogger getPersistentLogger() { - return persistentLogger; - } - // Loki private void initializeSecurityProvider() { @@ -279,8 +273,7 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc } private void initializeLogging() { - persistentLogger = new PersistentLogger(this); - Log.initialize(new AndroidLogger(), persistentLogger); + Log.initialize(new AndroidLogger()); } private void initializeCrashHandling() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/DatabaseFactory.java b/app/src/main/java/org/thoughtcrime/securesms/database/DatabaseFactory.java index 211223fd41..a5f9d4f171 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/DatabaseFactory.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/DatabaseFactory.java @@ -17,16 +17,17 @@ package org.thoughtcrime.securesms.database; import android.content.Context; + import androidx.annotation.NonNull; + import net.sqlcipher.database.SQLiteDatabase; + import org.thoughtcrime.securesms.attachments.DatabaseAttachmentProvider; import org.thoughtcrime.securesms.crypto.AttachmentSecret; import org.thoughtcrime.securesms.crypto.AttachmentSecretProvider; import org.thoughtcrime.securesms.crypto.DatabaseSecret; import org.thoughtcrime.securesms.crypto.DatabaseSecretProvider; import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper; -import org.thoughtcrime.securesms.database.LokiAPIDatabase; -import org.thoughtcrime.securesms.database.SessionJobDatabase; public class DatabaseFactory { @@ -35,28 +36,28 @@ public class DatabaseFactory { private static DatabaseFactory instance; private final SQLCipherOpenHelper databaseHelper; - private final SmsDatabase sms; - private final MmsDatabase mms; - private final AttachmentDatabase attachments; - private final MediaDatabase media; - private final ThreadDatabase thread; - private final MmsSmsDatabase mmsSmsDatabase; - private final DraftDatabase draftDatabase; - private final PushDatabase pushDatabase; - private final GroupDatabase groupDatabase; - private final RecipientDatabase recipientDatabase; - private final GroupReceiptDatabase groupReceiptDatabase; - private final SearchDatabase searchDatabase; - private final JobDatabase jobDatabase; - private final LokiAPIDatabase lokiAPIDatabase; - private final LokiMessageDatabase lokiMessageDatabase; - private final LokiThreadDatabase lokiThreadDatabase; - private final LokiUserDatabase lokiUserDatabase; - private final LokiBackupFilesDatabase lokiBackupFilesDatabase; - private final SessionJobDatabase sessionJobDatabase; - private final SessionContactDatabase sessionContactDatabase; - private final Storage storage; - private final DatabaseAttachmentProvider attachmentProvider; + private SmsDatabase sms; + private MmsDatabase mms; + private AttachmentDatabase attachments; + private MediaDatabase media; + private ThreadDatabase thread; + private MmsSmsDatabase mmsSmsDatabase; + private DraftDatabase draftDatabase; + private PushDatabase pushDatabase; + private GroupDatabase groupDatabase; + private RecipientDatabase recipientDatabase; + private GroupReceiptDatabase groupReceiptDatabase; + private SearchDatabase searchDatabase; + private JobDatabase jobDatabase; + private LokiAPIDatabase lokiAPIDatabase; + private LokiMessageDatabase lokiMessageDatabase; + private LokiThreadDatabase lokiThreadDatabase; + private LokiUserDatabase lokiUserDatabase; + private LokiBackupFilesDatabase lokiBackupFilesDatabase; + private SessionJobDatabase sessionJobDatabase; + private SessionContactDatabase sessionContactDatabase; + private Storage storage; + private DatabaseAttachmentProvider attachmentProvider; public static DatabaseFactory getInstance(Context context) { synchronized (lock) { @@ -68,55 +69,134 @@ public class DatabaseFactory { } public static MmsSmsDatabase getMmsSmsDatabase(Context context) { - return getInstance(context).mmsSmsDatabase; + DatabaseFactory factory = getInstance(context); + synchronized (lock) { + if (factory.mmsSmsDatabase == null) { + factory.mmsSmsDatabase = new MmsSmsDatabase(context, factory.databaseHelper); + } + return factory.mmsSmsDatabase; + } } public static ThreadDatabase getThreadDatabase(Context context) { - return getInstance(context).thread; + DatabaseFactory factory = getInstance(context); + synchronized (lock) { + if (factory.thread == null) { + factory.thread = new ThreadDatabase(context, factory.databaseHelper); + } + return factory.thread; + } } public static SmsDatabase getSmsDatabase(Context context) { - return getInstance(context).sms; + DatabaseFactory factory = getInstance(context); + synchronized (lock) { + if (factory.sms == null) { + factory.sms = new SmsDatabase(context, factory.databaseHelper); + } + return factory.sms; + } } public static MmsDatabase getMmsDatabase(Context context) { - return getInstance(context).mms; + DatabaseFactory factory = getInstance(context); + synchronized (lock) { + if (factory.mms == null) { + factory.mms = new MmsDatabase(context, factory.databaseHelper); + } + return factory.mms; + } } public static AttachmentDatabase getAttachmentDatabase(Context context) { - return getInstance(context).attachments; + DatabaseFactory factory = getInstance(context); + synchronized (lock) { + if (factory.attachments == null) { + AttachmentSecret attachmentSecret = AttachmentSecretProvider.getInstance(context).getOrCreateAttachmentSecret(); + factory.attachments = new AttachmentDatabase(context, factory.databaseHelper, attachmentSecret); + } + return factory.attachments; + } } public static MediaDatabase getMediaDatabase(Context context) { - return getInstance(context).media; + DatabaseFactory factory = getInstance(context); + synchronized (lock) { + if (factory.media == null) { + factory.media = new MediaDatabase(context, factory.databaseHelper); + } + return factory.media; + } } public static DraftDatabase getDraftDatabase(Context context) { - return getInstance(context).draftDatabase; + DatabaseFactory factory = getInstance(context); + synchronized (lock) { + if (factory.draftDatabase == null) { + factory.draftDatabase = new DraftDatabase(context, factory.databaseHelper); + } + return factory.draftDatabase; + } } public static PushDatabase getPushDatabase(Context context) { - return getInstance(context).pushDatabase; + DatabaseFactory factory = getInstance(context); + synchronized (lock) { + if (factory.pushDatabase == null) { + factory.pushDatabase = new PushDatabase(context, factory.databaseHelper); + } + return factory.pushDatabase; + } } public static GroupDatabase getGroupDatabase(Context context) { - return getInstance(context).groupDatabase; + DatabaseFactory factory = getInstance(context); + synchronized (lock) { + if (factory.groupDatabase == null) { + factory.groupDatabase = new GroupDatabase(context, factory.databaseHelper); + } + return factory.groupDatabase; + } } public static RecipientDatabase getRecipientDatabase(Context context) { - return getInstance(context).recipientDatabase; + DatabaseFactory factory = getInstance(context); + synchronized (lock) { + if (factory.recipientDatabase == null) { + factory.recipientDatabase = new RecipientDatabase(context, factory.databaseHelper); + } + return factory.recipientDatabase; + } } public static GroupReceiptDatabase getGroupReceiptDatabase(Context context) { - return getInstance(context).groupReceiptDatabase; + DatabaseFactory factory = getInstance(context); + synchronized (lock) { + if (factory.groupReceiptDatabase == null) { + factory.groupReceiptDatabase = new GroupReceiptDatabase(context, factory.databaseHelper); + } + return factory.groupReceiptDatabase; + } } public static SearchDatabase getSearchDatabase(Context context) { - return getInstance(context).searchDatabase; + DatabaseFactory factory = getInstance(context); + synchronized (lock) { + if (factory.searchDatabase == null) { + factory.searchDatabase = new SearchDatabase(context, factory.databaseHelper); + } + return factory.searchDatabase; + } } public static JobDatabase getJobDatabase(Context context) { - return getInstance(context).jobDatabase; + DatabaseFactory factory = getInstance(context); + synchronized (lock) { + if (factory.jobDatabase == null) { + factory.jobDatabase = new JobDatabase(context, factory.databaseHelper); + } + return factory.jobDatabase; + } } public static SQLiteDatabase getBackupDatabase(Context context) { @@ -125,41 +205,95 @@ public class DatabaseFactory { // region Loki public static LokiAPIDatabase getLokiAPIDatabase(Context context) { - return getInstance(context).lokiAPIDatabase; + DatabaseFactory factory = getInstance(context); + synchronized (lock) { + if (factory.lokiAPIDatabase == null) { + factory.lokiAPIDatabase = new LokiAPIDatabase(context, factory.databaseHelper); + } + return factory.lokiAPIDatabase; + } } public static LokiMessageDatabase getLokiMessageDatabase(Context context) { - return getInstance(context).lokiMessageDatabase; + DatabaseFactory factory = getInstance(context); + synchronized (lock) { + if (factory.lokiMessageDatabase == null) { + factory.lokiMessageDatabase = new LokiMessageDatabase(context, factory.databaseHelper); + } + return factory.lokiMessageDatabase; + } } public static LokiThreadDatabase getLokiThreadDatabase(Context context) { - return getInstance(context).lokiThreadDatabase; + DatabaseFactory factory = getInstance(context); + synchronized (lock) { + if (factory.lokiThreadDatabase == null) { + factory.lokiThreadDatabase = new LokiThreadDatabase(context, factory.databaseHelper); + } + return factory.lokiThreadDatabase; + } } public static LokiUserDatabase getLokiUserDatabase(Context context) { - return getInstance(context).lokiUserDatabase; + DatabaseFactory factory = getInstance(context); + synchronized (lock) { + if (factory.lokiUserDatabase == null) { + factory.lokiUserDatabase = new LokiUserDatabase(context, factory.databaseHelper); + } + return factory.lokiUserDatabase; + } } public static LokiBackupFilesDatabase getLokiBackupFilesDatabase(Context context) { - return getInstance(context).lokiBackupFilesDatabase; + DatabaseFactory factory = getInstance(context); + synchronized (lock) { + if (factory.lokiBackupFilesDatabase == null) { + factory.lokiBackupFilesDatabase = new LokiBackupFilesDatabase(context, factory.databaseHelper); + } + return factory.lokiBackupFilesDatabase; + } } public static SessionJobDatabase getSessionJobDatabase(Context context) { - return getInstance(context).sessionJobDatabase; + DatabaseFactory factory = getInstance(context); + synchronized (lock) { + if (factory.sessionJobDatabase == null) { + factory.sessionJobDatabase = new SessionJobDatabase(context, factory.databaseHelper); + } + return factory.sessionJobDatabase; + } } public static SessionContactDatabase getSessionContactDatabase(Context context) { - return getInstance(context).sessionContactDatabase; + DatabaseFactory factory = getInstance(context); + synchronized (lock) { + if (factory.sessionContactDatabase == null) { + factory.sessionContactDatabase = new SessionContactDatabase(context, factory.databaseHelper); + } + return factory.sessionContactDatabase; + } } // endregion // region Refactor public static Storage getStorage(Context context) { - return getInstance(context).storage; + DatabaseFactory factory = getInstance(context); + synchronized (lock) { + if (factory.storage == null) { + factory.storage = new Storage(context, factory.databaseHelper); + } + return factory.storage; + } } public static DatabaseAttachmentProvider getAttachmentProvider(Context context) { - return getInstance(context).attachmentProvider; + DatabaseFactory factory = getInstance(context); + synchronized (lock) { + if (factory.attachmentProvider == null) { + factory.attachmentProvider = new DatabaseAttachmentProvider(context, factory.databaseHelper); + } + return factory.attachmentProvider; + } } // endregion @@ -172,31 +306,8 @@ public class DatabaseFactory { SQLiteDatabase.loadLibs(context); DatabaseSecret databaseSecret = new DatabaseSecretProvider(context).getOrCreateDatabaseSecret(); - AttachmentSecret attachmentSecret = AttachmentSecretProvider.getInstance(context).getOrCreateAttachmentSecret(); this.databaseHelper = new SQLCipherOpenHelper(context, databaseSecret); - this.sms = new SmsDatabase(context, databaseHelper); - this.mms = new MmsDatabase(context, databaseHelper); - this.attachments = new AttachmentDatabase(context, databaseHelper, attachmentSecret); - this.media = new MediaDatabase(context, databaseHelper); - this.thread = new ThreadDatabase(context, databaseHelper); - this.mmsSmsDatabase = new MmsSmsDatabase(context, databaseHelper); - this.draftDatabase = new DraftDatabase(context, databaseHelper); - this.pushDatabase = new PushDatabase(context, databaseHelper); - this.groupDatabase = new GroupDatabase(context, databaseHelper); - this.recipientDatabase = new RecipientDatabase(context, databaseHelper); - this.groupReceiptDatabase = new GroupReceiptDatabase(context, databaseHelper); - this.searchDatabase = new SearchDatabase(context, databaseHelper); - this.jobDatabase = new JobDatabase(context, databaseHelper); - this.lokiAPIDatabase = new LokiAPIDatabase(context, databaseHelper); - this.lokiMessageDatabase = new LokiMessageDatabase(context, databaseHelper); - this.lokiThreadDatabase = new LokiThreadDatabase(context, databaseHelper); - this.lokiUserDatabase = new LokiUserDatabase(context, databaseHelper); - this.lokiBackupFilesDatabase = new LokiBackupFilesDatabase(context, databaseHelper); - this.storage = new Storage(context, databaseHelper); - this.attachmentProvider = new DatabaseAttachmentProvider(context, databaseHelper); - this.sessionJobDatabase = new SessionJobDatabase(context, databaseHelper); - this.sessionContactDatabase = new SessionContactDatabase(context, databaseHelper); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/logging/PersistentLogger.java b/app/src/main/java/org/thoughtcrime/securesms/logging/PersistentLogger.java deleted file mode 100644 index ecb4b68983..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/logging/PersistentLogger.java +++ /dev/null @@ -1,244 +0,0 @@ -package org.thoughtcrime.securesms.logging; - -import android.content.Context; -import androidx.annotation.AnyThread; -import androidx.annotation.WorkerThread; - -import org.session.libsignal.utilities.NoExternalStorageException; - -import org.session.libsignal.utilities.ListenableFuture; -import org.session.libsignal.utilities.SettableFuture; -import org.session.libsignal.utilities.Log; - -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 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 getLogs() { - final SettableFuture future = new SettableFuture<>(); - - executor.execute(() -> { - StringBuilder builder = new StringBuilder(); - - try { - File[] logs = getSortedLogFiles(); - for (int i = logs.length - 1; i >= 0; i--) { - try { - LogFile.Reader reader = new LogFile.Reader(secret, logs[i]); - builder.append(reader.readAll()); - } catch (IOException e) { - android.util.Log.w(TAG, "Failed to read log at index " + i + ". Removing reference."); - logs[i].delete(); - } - } - - future.set(builder.toString()); - } catch (NoExternalStorageException e) { - future.setException(e); - } - }); - - return future; - } - - @WorkerThread - private void initializeWriter() { - try { - writer = new LogFile.Writer(secret, getOrCreateActiveLogFile()); - } catch (NoExternalStorageException | IOException e) { - android.util.Log.e(TAG, "Failed to initialize writer.", e); - } - } - - @AnyThread - private void write(String level, String tag, String message, Throwable t) { - executor.execute(() -> { - try { - if (writer == null) { - return; - } - - if (writer.getLogSize() >= MAX_LOG_SIZE) { - writer.close(); - writer = new LogFile.Writer(secret, createNewLogFile()); - trimLogFilesOverMax(); - } - - for (String entry : buildLogEntries(level, tag, message, t)) { - writer.writeEntry(entry); - } - - } catch (NoExternalStorageException e) { - android.util.Log.w(TAG, "Cannot persist logs.", e); - } catch (IOException e) { - android.util.Log.w(TAG, "Failed to write line. Deleting all logs and starting over."); - deleteAllLogs(); - initializeWriter(); - } - }); - } - - private void trimLogFilesOverMax() throws NoExternalStorageException { - File[] logs = getSortedLogFiles(); - if (logs.length > MAX_LOG_FILES) { - for (int i = MAX_LOG_FILES; i < logs.length; i++) { - logs[i].delete(); - } - } - } - - private void deleteAllLogs() { - try { - File[] logs = getSortedLogFiles(); - for (File log : logs) { - log.delete(); - } - } catch (NoExternalStorageException e) { - android.util.Log.w(TAG, "Was unable to delete logs.", e); - } - } - - private File getOrCreateActiveLogFile() throws NoExternalStorageException { - File[] logs = getSortedLogFiles(); - if (logs.length > 0) { - return logs[0]; - } - - return createNewLogFile(); - } - - private File createNewLogFile() throws NoExternalStorageException { - return new File(getOrCreateLogDirectory(), FILENAME_PREFIX + System.currentTimeMillis()); - } - - private File[] getSortedLogFiles() throws NoExternalStorageException { - File[] logs = getOrCreateLogDirectory().listFiles(); - if (logs != null) { - Arrays.sort(logs, (o1, o2) -> o2.getName().compareTo(o1.getName())); - return logs; - } - return new File[0]; - } - - private File getOrCreateLogDirectory() throws NoExternalStorageException { - File logDir = new File(context.getCacheDir(), LOG_DIRECTORY); - if (!logDir.exists() && !logDir.mkdir()) { - throw new NoExternalStorageException("Unable to create log directory."); - } - - return logDir; - } - - private List buildLogEntries(String level, String tag, String message, Throwable t) { - List entries = new LinkedList<>(); - Date date = new Date(); - - entries.add(buildEntry(level, tag, message, date)); - - if (t != null) { - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - t.printStackTrace(new PrintStream(outputStream)); - - String trace = new String(outputStream.toByteArray()); - String[] lines = trace.split("\\n"); - - for (String line : lines) { - entries.add(buildEntry(level, tag, line, date)); - } - } - - return entries; - } - - private String buildEntry(String level, String tag, String message, Date date) { - return DATE_FORMAT.format(date) + ' ' + level + ' ' + tag + ": " + message; - } -} diff --git a/libsignal/build.gradle b/libsignal/build.gradle index 5fa6d19ca2..91e50a37d5 100644 --- a/libsignal/build.gradle +++ b/libsignal/build.gradle @@ -4,6 +4,10 @@ apply plugin: 'kotlin-android' android { compileSdkVersion androidCompileSdkVersion + defaultConfig { + minSdkVersion androidMinimumSdkVersion + } + compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8