diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/OptimizedMessageNotifier.java b/app/src/main/java/org/thoughtcrime/securesms/notifications/OptimizedMessageNotifier.java index b784acdc5..4e109ef66 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/OptimizedMessageNotifier.java +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/OptimizedMessageNotifier.java @@ -8,6 +8,7 @@ import androidx.annotation.NonNull; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.util.LeakyBucketLimiter; +import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.concurrent.SignalExecutors; /** @@ -51,26 +52,37 @@ public class OptimizedMessageNotifier implements MessageNotifier { @Override public void updateNotification(@NonNull Context context) { - limiter.run(() -> wrapped.updateNotification(context)); + runOnLimiter(() -> wrapped.updateNotification(context)); } @Override public void updateNotification(@NonNull Context context, long threadId) { - limiter.run(() -> wrapped.updateNotification(context, threadId)); + runOnLimiter(() -> wrapped.updateNotification(context, threadId)); } @Override public void updateNotification(@NonNull Context context, long threadId, boolean signal) { - limiter.run(() -> wrapped.updateNotification(context, threadId, signal)); + runOnLimiter(() -> wrapped.updateNotification(context, threadId, signal)); } @Override public void updateNotification(@NonNull Context context, long threadId, boolean signal, int reminderCount) { - limiter.run(() -> wrapped.updateNotification(context, threadId, signal, reminderCount)); + runOnLimiter(() -> wrapped.updateNotification(context, threadId, signal, reminderCount)); } @Override public void clearReminder(@NonNull Context context) { wrapped.clearReminder(context); } + + private void runOnLimiter(@NonNull Runnable runnable) { + Throwable prettyException = new Throwable(); + limiter.run(() -> { + try { + runnable.run(); + } catch (RuntimeException e) { + throw Util.appendStackTrace(e, prettyException); + } + }); + } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/Util.java b/app/src/main/java/org/thoughtcrime/securesms/util/Util.java index cc2d04c60..b5a2e5556 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/Util.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/Util.java @@ -627,4 +627,25 @@ public class Util { return false; } } + + /** + * Appends the stack trace of the provided throwable onto the provided primary exception. This is + * useful for when exceptions are thrown inside of asynchronous systems (like runnables in an + * executor) where you'd otherwise lose important parts of the stack trace. This lets you save a + * throwable at the entry point, and then combine it with any caught exceptions later. + * + * @return The provided primary exception, for convenience. + */ + public static RuntimeException appendStackTrace(@NonNull RuntimeException primary, @NonNull Throwable secondary) { + StackTraceElement[] now = primary.getStackTrace(); + StackTraceElement[] then = secondary.getStackTrace(); + StackTraceElement[] combined = new StackTraceElement[now.length + then.length]; + + System.arraycopy(now, 0, combined, 0, now.length); + System.arraycopy(then, 0, combined, now.length, then.length); + + primary.setStackTrace(combined); + + return primary; + } }