diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentTable.kt index c9a571ae05..1cb6b846de 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentTable.kt @@ -3557,38 +3557,41 @@ class AttachmentTable( ) .readToSingleLong(0) - val archiveStatusMediaNameCounts: Map = ArchiveTransferState.entries.associateWith { state -> + val archiveStatusMediaNameCounts: Map = ArchiveTransferState.entries.associateWith { 0L }.toMutableMap().apply { readableDatabase.query( """ - SELECT COUNT(*) FROM ( - SELECT DISTINCT $DATA_HASH_END, $REMOTE_KEY + SELECT $ARCHIVE_TRANSFER_STATE, COUNT(*) FROM ( + SELECT DISTINCT $DATA_HASH_END, $REMOTE_KEY, $ARCHIVE_TRANSFER_STATE FROM $TABLE_NAME LEFT JOIN ${MessageTable.TABLE_NAME} ON $TABLE_NAME.$MESSAGE_ID = ${MessageTable.TABLE_NAME}.${MessageTable.ID} - WHERE ${buildAttachmentsThatCanArchiveQuery(archiveTransferStateFilter = "$ARCHIVE_TRANSFER_STATE = ${state.value}")} - ) + WHERE ${buildAttachmentsThatCanArchiveQuery(archiveTransferStateFilter = "1=1")} + ) GROUP BY $ARCHIVE_TRANSFER_STATE """ - ) - .readToSingleLong(0) + ).forEach { cursor -> + this[ArchiveTransferState.deserialize(cursor.getInt(0))] = cursor.getLong(1) + } } val uniqueEligibleMediaNamesWithThumbnailsCount = readableDatabase.query("SELECT COUNT(*) FROM (SELECT DISTINCT $DATA_HASH_END, $REMOTE_KEY FROM $TABLE_NAME WHERE $DATA_HASH_END NOT NULL AND $REMOTE_KEY NOT NULL AND $THUMBNAIL_FILE NOT NULL AND $QUOTE = 0 AND $MESSAGE_ID != $WALLPAPER_MESSAGE_ID)") .readToSingleLong(-1L) - val archiveStatusMediaNameThumbnailCounts: Map = ArchiveTransferState.entries.associateWith { state -> + + val archiveStatusMediaNameThumbnailCounts: Map = ArchiveTransferState.entries.associateWith { 0L }.toMutableMap().apply { readableDatabase.query( """ - SELECT COUNT(*) FROM ( - SELECT DISTINCT $DATA_HASH_END, $REMOTE_KEY + SELECT $ARCHIVE_THUMBNAIL_TRANSFER_STATE, COUNT(*) FROM ( + SELECT DISTINCT $DATA_HASH_END, $REMOTE_KEY, $ARCHIVE_THUMBNAIL_TRANSFER_STATE FROM $TABLE_NAME LEFT JOIN ${MessageTable.TABLE_NAME} ON $TABLE_NAME.$MESSAGE_ID = ${MessageTable.TABLE_NAME}.${MessageTable.ID} - WHERE - ${buildAttachmentsThatCanArchiveQuery("$ARCHIVE_THUMBNAIL_TRANSFER_STATE = ${state.value}")} AND + WHERE + ${buildAttachmentsThatCanArchiveQuery("1=1")} AND $QUOTE = 0 AND ($CONTENT_TYPE LIKE 'image/%' OR $CONTENT_TYPE LIKE 'video/%') AND $CONTENT_TYPE != 'image/svg+xml' AND $MESSAGE_ID != $WALLPAPER_MESSAGE_ID - ) + ) GROUP BY $ARCHIVE_THUMBNAIL_TRANSFER_STATE """ - ) - .readToSingleLong(0) + ).forEach { cursor -> + this[ArchiveTransferState.deserialize(cursor.getInt(0))] = cursor.getLong(1) + } } val pendingAttachmentUploadBytes = getPendingArchiveUploadBytes() @@ -3599,9 +3602,9 @@ class AttachmentTable( FROM ( SELECT DISTINCT $DATA_HASH_END, $REMOTE_KEY, $DATA_SIZE FROM $TABLE_NAME - WHERE - $DATA_FILE NOT NULL AND - $DATA_HASH_END NOT NULL AND + WHERE + $DATA_FILE NOT NULL AND + $DATA_HASH_END NOT NULL AND $REMOTE_KEY NOT NULL AND $ARCHIVE_TRANSFER_STATE = ${ArchiveTransferState.FINISHED.value} ) diff --git a/app/src/main/java/org/thoughtcrime/securesms/logsubmit/LogSectionBadges.java b/app/src/main/java/org/thoughtcrime/securesms/logsubmit/LogSectionBadges.java index 28da91dff7..f74ea4a81b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/logsubmit/LogSectionBadges.java +++ b/app/src/main/java/org/thoughtcrime/securesms/logsubmit/LogSectionBadges.java @@ -33,6 +33,11 @@ final class LogSectionBadges implements LogSection { InAppPaymentTable.InAppPayment latestRecurringDonation = SignalDatabase.inAppPayments().getLatestInAppPaymentByType(InAppPaymentType.RECURRING_DONATION); if (latestRecurringDonation != null) { + InAppPaymentSubscriberRecord donationSubscriber = InAppPaymentsRepository.getSubscriber(InAppPaymentSubscriberRecord.Type.DONATION); + boolean shouldCancel = donationSubscriber != null + ? donationSubscriber.getRequiresCancel() + : SignalStore.inAppPayments().getShouldCancelSubscriptionBeforeNextSubscribeAttempt(); + return new StringBuilder().append("Badge Count : ").append(Recipient.self().getBadges().size()).append("\n") .append("ExpiredBadge : ").append(SignalStore.inAppPayments().getExpiredBadge() != null).append("\n") .append("LastKeepAliveLaunchTime : ").append(SignalStore.inAppPayments().getLastKeepAliveLaunchTime()).append("\n") @@ -44,7 +49,7 @@ final class LogSectionBadges implements LogSection { .append("InAppPaymentData.Error : ").append(getError(latestRecurringDonation.getData())).append("\n") .append("InAppPaymentData.Cancellation : ").append(getCancellation(latestRecurringDonation.getData())).append("\n") .append("DisplayBadgesOnProfile : ").append(SignalStore.inAppPayments().getDisplayBadgesOnProfile()).append("\n") - .append("ShouldCancelBeforeNextAttempt : ").append(InAppPaymentsRepository.getShouldCancelSubscriptionBeforeNextSubscribeAttempt(InAppPaymentSubscriberRecord.Type.DONATION)).append("\n") + .append("ShouldCancelBeforeNextAttempt : ").append(shouldCancel).append("\n") .append("IsUserManuallyCancelledDonation : ").append(SignalStore.inAppPayments().isDonationSubscriptionManuallyCancelled()).append("\n"); } else { diff --git a/app/src/main/java/org/thoughtcrime/securesms/logsubmit/LogSectionRemoteBackups.kt b/app/src/main/java/org/thoughtcrime/securesms/logsubmit/LogSectionRemoteBackups.kt index fd2dcd2853..dd8ae2b003 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/logsubmit/LogSectionRemoteBackups.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/logsubmit/LogSectionRemoteBackups.kt @@ -105,7 +105,12 @@ class LogSectionRemoteBackups : LogSection { } output.append("\n -- Attachment Stats\n") - output.append(SignalDatabase.attachments.debugGetAttachmentStats().prettyString()) + val backupInProgress = SignalStore.backup.archiveUploadState?.state?.let { it != ArchiveUploadProgressState.State.None && it != ArchiveUploadProgressState.State.UserCanceled } ?: false + if (SignalStore.backup.hasBackupCreationError || backupInProgress) { + output.append(SignalDatabase.attachments.debugGetAttachmentStats().prettyString()) + } else { + output.append("Skipped (last backup succeeded and no upload in progress)\n") + } return output } diff --git a/app/src/main/java/org/thoughtcrime/securesms/logsubmit/SubmitDebugLogActivity.java b/app/src/main/java/org/thoughtcrime/securesms/logsubmit/SubmitDebugLogActivity.java index 9edf5d2b3e..503c2201c3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/logsubmit/SubmitDebugLogActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/logsubmit/SubmitDebugLogActivity.java @@ -354,6 +354,16 @@ public class SubmitDebugLogActivity extends BaseActivity { private void initViewModel() { viewModel.getMode().observe(this, this::presentMode); viewModel.getEvents().observe(this, this::presentEvents); + viewModel.getSlowPrefixWarning().observe(this, this::presentSlowPrefixWarning); + } + + private void presentSlowPrefixWarning(@NonNull Long durationMillis) { + int durationSeconds = (int) Math.round(durationMillis / 1000.0); + new MaterialAlertDialogBuilder(this) + .setTitle(R.string.SubmitDebugLogActivity_slow_log_title) + .setMessage(getString(R.string.SubmitDebugLogActivity_slow_log_message, durationSeconds)) + .setPositiveButton(android.R.string.ok, null) + .show(); } private void subscribeToLogLines() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/logsubmit/SubmitDebugLogRepository.java b/app/src/main/java/org/thoughtcrime/securesms/logsubmit/SubmitDebugLogRepository.java index affae85166..2ab0b9be6f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/logsubmit/SubmitDebugLogRepository.java +++ b/app/src/main/java/org/thoughtcrime/securesms/logsubmit/SubmitDebugLogRepository.java @@ -373,18 +373,28 @@ public class SubmitDebugLogRepository { int maxTitleLength = SECTIONS.stream().reduce(0, (max, section) -> Math.max(max, section.getTitle().length()), Integer::sum); - List allLines = new ArrayList<>(); - + List>> futures = new ArrayList<>(SECTIONS.size()); for (LogSection section : SECTIONS) { - List lines = getLinesForSection(context, section, maxTitleLength); + futures.add(SignalExecutors.BOUNDED.submit(() -> getLinesForSection(context, section, maxTitleLength))); + } - if (SECTIONS.indexOf(section) != SECTIONS.size() - 1) { - for (int i = 0; i < SECTION_SPACING; i++) { - lines.add(SimpleLogLine.EMPTY); - } + List allLines = new ArrayList<>(); + for (int i = 0; i < futures.size(); i++) { + List lines; + try { + lines = futures.get(i).get(); + } catch (InterruptedException | ExecutionException e) { + Log.w(TAG, "Failed to read section " + SECTIONS.get(i).getTitle(), e); + lines = new ArrayList<>(); } allLines.addAll(lines); + + if (i != futures.size() - 1) { + for (int j = 0; j < SECTION_SPACING; j++) { + allLines.add(SimpleLogLine.EMPTY); + } + } } List withIds = new ArrayList<>(allLines.size()); diff --git a/app/src/main/java/org/thoughtcrime/securesms/logsubmit/SubmitDebugLogViewModel.java b/app/src/main/java/org/thoughtcrime/securesms/logsubmit/SubmitDebugLogViewModel.java index 3d566f9f21..635279d992 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/logsubmit/SubmitDebugLogViewModel.java +++ b/app/src/main/java/org/thoughtcrime/securesms/logsubmit/SubmitDebugLogViewModel.java @@ -15,6 +15,7 @@ import org.signal.core.util.tracing.Tracer; import org.signal.debuglogsviewer.DebugLogsViewer; import org.thoughtcrime.securesms.database.LogDatabase; import org.thoughtcrime.securesms.dependencies.AppDependencies; +import org.thoughtcrime.securesms.util.RemoteConfig; import org.thoughtcrime.securesms.util.SingleLiveEvent; import java.util.ArrayList; @@ -28,20 +29,23 @@ public class SubmitDebugLogViewModel extends ViewModel { private static final String TAG = Log.tag(SubmitDebugLogViewModel.class); - private static final int CHUNK_SIZE = 10_000; + private static final int CHUNK_SIZE = 10_000; + private static final long SLOW_PREFIX_THRESHOLD_MILLIS = 3_000L; private final SubmitDebugLogRepository repo; private final MutableLiveData mode; private final SingleLiveEvent event; + private final SingleLiveEvent slowPrefixWarning; private final long firstViewTime; private final byte[] trace; private SubmitDebugLogViewModel() { - this.repo = new SubmitDebugLogRepository(); - this.mode = new MutableLiveData<>(); - this.trace = Tracer.getInstance().serialize(); - this.firstViewTime = System.currentTimeMillis(); - this.event = new SingleLiveEvent<>(); + this.repo = new SubmitDebugLogRepository(); + this.mode = new MutableLiveData<>(); + this.trace = Tracer.getInstance().serialize(); + this.firstViewTime = System.currentTimeMillis(); + this.event = new SingleLiveEvent<>(); + this.slowPrefixWarning = new SingleLiveEvent<>(); } @NonNull Observable> getLogLinesObservable() { @@ -50,7 +54,13 @@ public class SubmitDebugLogViewModel extends ViewModel { try { mode.postValue(Mode.LOADING); + long prefixStartTime = System.currentTimeMillis(); repo.getPrefixLogLines(prefixLines -> { + long prefixDurationMillis = System.currentTimeMillis() - prefixStartTime; + if (prefixDurationMillis > SLOW_PREFIX_THRESHOLD_MILLIS && RemoteConfig.showSlowDebugLogWarning()) { + slowPrefixWarning.postValue(prefixDurationMillis); + } + try { List prefixStrings = new ArrayList<>(); for (LogLine line : prefixLines) { @@ -142,6 +152,10 @@ public class SubmitDebugLogViewModel extends ViewModel { return event; } + @NonNull LiveData getSlowPrefixWarning() { + return slowPrefixWarning; + } + void onDiskSaveLocationReady(@Nullable Uri uri) { if (uri == null) { Log.w(TAG, "Null URI!"); diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/RemoteConfig.kt b/app/src/main/java/org/thoughtcrime/securesms/util/RemoteConfig.kt index 21573a329e..ee5a93c686 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/RemoteConfig.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/util/RemoteConfig.kt @@ -674,6 +674,15 @@ object RemoteConfig { hotSwappable = true ) + /** Whether to surface a warning dialog when debug log prefix generation exceeds a threshold. */ + @JvmStatic + @get:JvmName("showSlowDebugLogWarning") + val showSlowDebugLogWarning: Boolean by remoteBoolean( + key = "android.showSlowDebugLogWarning", + defaultValue = false, + hotSwappable = true + ) + /** How often we allow an automatic session reset. */ @JvmStatic @get:JvmName("automaticSessionResetIntervalSeconds") diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f4b80f296a..0c604c5ef8 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -3114,6 +3114,10 @@ Info Warn Error + + Slow log generation + + Generating the debug log header took %1$d seconds. We should figure out what\'s slowing things down. support@signal.org