Skip to content

[MEDIUM] Grouped message notifications rebuild an unbounded MessagingStyle history on every post — per-message Binder IPC and TransactionTooLargeException risk #182

@erskingardner

Description

@erskingardner

Severity: MEDIUM

Summary

The per-conversation notification stacking introduced in #180 rebuilds each conversation's MessagingStyle by re-extracting the entire prior history from the currently-posted notification and appending the new message. The extracted history is never trimmed, so for a busy conversation the rebuilt notification accumulates an ever-growing list of MessagingStyle.Message entries that are re-serialized across the Binder boundary on every single post.

Evidence

app/src/main/java/dev/ipf/darkmatter/notifications/LocalNotificationPresenter.kt

private fun messagingStyle(...): NotificationCompat.MessagingStyle {
    ...
    val style = existingMessagingStyle(content.notificationTag)
        ?: NotificationCompat.MessagingStyle(self)
    ...
    style.addMessage(content.body, update.timestampMs, sender)   // appended; nothing trimmed
    return style
}

private fun existingMessagingStyle(tag: String): NotificationCompat.MessagingStyle? {
    val manager = context.getSystemService(NotificationManager::class.java) ?: return null
    val existing =
        runCatching { manager.activeNotifications }                // Binder IPC into NotificationManagerService
            .getOrNull()
            ?.firstOrNull { it.tag == tag && it.id == MESSAGE_NOTIFICATION_ID }
            ?: return null
    return NotificationCompat.MessagingStyle.extractMessagingStyleFromNotification(existing.notification)
}

Every incoming message:

  1. Calls manager.activeNotifications — a Binder IPC into NotificationManagerService — on each post.
  2. Re-extracts the full prior MessagingStyle and appends, so the message list grows without bound for the life of the (un-dismissed) notification.

Impact

  • On a high-traffic conversation this is O(n) work and an increasingly large parcel per notification. Once the accumulated message list (each entry carrying body text + a Person) exceeds the ~1 MB Binder limit, notify() throws TransactionTooLargeException and the notification is silently dropped — the user stops getting notifications for an active conversation.
  • Steady per-message IPC + re-serialization cost on the notification path.

Suggested fix

Cap the retained history when rebuilding — the system only renders ~6–7 lines, so keep only the last N messages (e.g. trim the extracted list, or build a fresh MessagingStyle seeded with a bounded recent set) rather than re-extracting and re-appending the unbounded prior history.

Validation

Post many messages (e.g. 200+) to one conversation without dismissing the notification; confirm the notification keeps updating and the parcel size stays bounded (no TransactionTooLargeException in logcat).

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions