Fix too many pending intents crashes.

This commit is contained in:
Cody Henthorne
2022-09-26 11:20:47 -04:00
parent 2a82db2b02
commit ff28ff0e6b
6 changed files with 106 additions and 55 deletions

View File

@@ -285,16 +285,18 @@ class DefaultMessageNotifier(context: Application) : MessageNotifier {
}
val alarmManager: AlarmManager? = ContextCompat.getSystemService(context, AlarmManager::class.java)
val pendingIntent: PendingIntent = PendingIntent.getBroadcast(context, 0, Intent(context, ReminderReceiver::class.java), PendingIntentFlags.updateCurrent())
alarmManager?.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + timeout, pendingIntent)
lastScheduledReminder = System.currentTimeMillis()
val pendingIntent: PendingIntent? = NotificationPendingIntentHelper.getBroadcast(context, 0, Intent(context, ReminderReceiver::class.java), PendingIntentFlags.updateCurrent())
if (pendingIntent != null) {
alarmManager?.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + timeout, pendingIntent)
lastScheduledReminder = System.currentTimeMillis()
}
}
private fun clearReminderInternal(context: Context) {
lastScheduledReminder = 0
threadReminders.clear()
val pendingIntent: PendingIntent? = PendingIntent.getBroadcast(context, 0, Intent(context, ReminderReceiver::class.java), PendingIntentFlags.cancelCurrent())
val pendingIntent: PendingIntent? = NotificationPendingIntentHelper.getBroadcast(context, 0, Intent(context, ReminderReceiver::class.java), PendingIntentFlags.cancelCurrent())
if (pendingIntent != null) {
val alarmManager: AlarmManager? = ContextCompat.getSystemService(context, AlarmManager::class.java)
alarmManager?.cancel(pendingIntent)

View File

@@ -70,7 +70,7 @@ sealed class NotificationBuilder(protected val context: Context) {
abstract fun setPriority(priority: Int)
abstract fun setAlarms(recipient: Recipient?)
abstract fun setTicker(ticker: CharSequence?)
abstract fun addTurnOffJoinedNotificationsAction(pendingIntent: PendingIntent)
abstract fun addTurnOffJoinedNotificationsAction(pendingIntent: PendingIntent?)
abstract fun setAutoCancel(autoCancel: Boolean)
abstract fun setLocusIdActual(locusId: String)
abstract fun build(): Notification
@@ -189,58 +189,72 @@ sealed class NotificationBuilder(protected val context: Context) {
val builder: NotificationCompat.Builder = NotificationCompat.Builder(context, NotificationChannels.getMessagesChannel(context))
override fun addActions(replyMethod: ReplyMethod, conversation: NotificationConversation) {
val markAsRead: PendingIntent = conversation.getMarkAsReadIntent(context)
val markAsReadAction: NotificationCompat.Action = NotificationCompat.Action.Builder(R.drawable.check, context.getString(R.string.MessageNotifier_mark_read), markAsRead)
.setSemanticAction(NotificationCompat.Action.SEMANTIC_ACTION_MARK_AS_READ)
.setShowsUserInterface(false)
.build()
val extender: NotificationCompat.WearableExtender = NotificationCompat.WearableExtender()
builder.addAction(markAsReadAction)
extender.addAction(markAsReadAction)
val markAsRead: PendingIntent? = conversation.getMarkAsReadIntent(context)
if (markAsRead != null) {
val markAsReadAction: NotificationCompat.Action =
NotificationCompat.Action.Builder(R.drawable.check, context.getString(R.string.MessageNotifier_mark_read), markAsRead)
.setSemanticAction(NotificationCompat.Action.SEMANTIC_ACTION_MARK_AS_READ)
.setShowsUserInterface(false)
.build()
builder.addAction(markAsReadAction)
extender.addAction(markAsReadAction)
}
if (conversation.mostRecentNotification.canReply(context)) {
val quickReply: PendingIntent = conversation.getQuickReplyIntent(context)
val remoteReply: PendingIntent = conversation.getRemoteReplyIntent(context, replyMethod)
val quickReply: PendingIntent? = conversation.getQuickReplyIntent(context)
val remoteReply: PendingIntent? = conversation.getRemoteReplyIntent(context, replyMethod)
val actionName: String = context.getString(R.string.MessageNotifier_reply)
val label: String = context.getString(replyMethod.toLongDescription())
val replyAction: NotificationCompat.Action = if (Build.VERSION.SDK_INT >= 24) {
val replyAction: NotificationCompat.Action? = if (Build.VERSION.SDK_INT >= 24 && remoteReply != null) {
NotificationCompat.Action.Builder(R.drawable.ic_reply_white_36dp, actionName, remoteReply)
.addRemoteInput(RemoteInput.Builder(DefaultMessageNotifier.EXTRA_REMOTE_REPLY).setLabel(label).build())
.setSemanticAction(NotificationCompat.Action.SEMANTIC_ACTION_REPLY)
.setShowsUserInterface(false)
.build()
} else {
} else if (quickReply != null) {
NotificationCompat.Action(R.drawable.ic_reply_white_36dp, actionName, quickReply)
} else {
null
}
val wearableReplyAction = NotificationCompat.Action.Builder(R.drawable.ic_reply, actionName, remoteReply)
.addRemoteInput(RemoteInput.Builder(DefaultMessageNotifier.EXTRA_REMOTE_REPLY).setLabel(label).build())
.build()
builder.addAction(replyAction)
extender.addAction(wearableReplyAction)
if (remoteReply != null) {
val wearableReplyAction = NotificationCompat.Action.Builder(R.drawable.ic_reply, actionName, remoteReply)
.addRemoteInput(RemoteInput.Builder(DefaultMessageNotifier.EXTRA_REMOTE_REPLY).setLabel(label).build())
.build()
extender.addAction(wearableReplyAction)
}
}
builder.extend(extender)
}
override fun addMarkAsReadActionActual(state: NotificationState) {
val markAllAsReadAction = NotificationCompat.Action(R.drawable.check, context.getString(R.string.MessageNotifier_mark_all_as_read), state.getMarkAsReadIntent(context))
builder.addAction(markAllAsReadAction)
builder.extend(NotificationCompat.WearableExtender().addAction(markAllAsReadAction))
val markAsRead: PendingIntent? = state.getMarkAsReadIntent(context)
if (markAsRead != null) {
val markAllAsReadAction = NotificationCompat.Action(R.drawable.check, context.getString(R.string.MessageNotifier_mark_all_as_read), markAsRead)
builder.addAction(markAllAsReadAction)
builder.extend(NotificationCompat.WearableExtender().addAction(markAllAsReadAction))
}
}
override fun addTurnOffJoinedNotificationsAction(pendingIntent: PendingIntent) {
val turnOffTheseNotifications = NotificationCompat.Action(
R.drawable.check,
context.getString(R.string.MessageNotifier_turn_off_these_notifications),
pendingIntent
)
override fun addTurnOffJoinedNotificationsAction(pendingIntent: PendingIntent?) {
if (pendingIntent != null) {
val turnOffTheseNotifications = NotificationCompat.Action(
R.drawable.check,
context.getString(R.string.MessageNotifier_turn_off_these_notifications),
pendingIntent
)
builder.addAction(turnOffTheseNotifications)
builder.addAction(turnOffTheseNotifications)
}
}
override fun addMessagesActual(conversation: NotificationConversation, includeShortcut: Boolean) {
@@ -338,20 +352,22 @@ sealed class NotificationBuilder(protected val context: Context) {
return
}
val intent = PendingIntent.getActivity(
val intent: PendingIntent? = NotificationPendingIntentHelper.getActivity(
context,
0,
ConversationIntents.createBubbleIntent(context, conversation.recipient.id, conversation.thread.threadId),
mutable()
)
val bubbleMetadata = NotificationCompat.BubbleMetadata.Builder(intent, AvatarUtil.getIconCompatForShortcut(context, conversation.recipient))
.setAutoExpandBubble(bubbleState === BubbleUtil.BubbleState.SHOWN)
.setDesiredHeight(600)
.setSuppressNotification(bubbleState === BubbleUtil.BubbleState.SHOWN)
.build()
if (intent != null) {
val bubbleMetadata = NotificationCompat.BubbleMetadata.Builder(intent, AvatarUtil.getIconCompatForShortcut(context, conversation.recipient))
.setAutoExpandBubble(bubbleState === BubbleUtil.BubbleState.SHOWN)
.setDesiredHeight(600)
.setSuppressNotification(bubbleState === BubbleUtil.BubbleState.SHOWN)
.build()
builder.bubbleMetadata = bubbleMetadata
builder.bubbleMetadata = bubbleMetadata
}
}
override fun setLights(@ColorInt color: Int, onTime: Int, offTime: Int) {

View File

@@ -140,7 +140,10 @@ data class NotificationConversation(
} catch (e: NullPointerException) {
Log.w(NotificationFactory.TAG, "Vivo device quirk sometimes throws NPE", e)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
PendingIntent.getActivity(context, 0, intent, PendingIntentFlags.updateCurrent())
NotificationPendingIntentHelper.getActivity(context, 0, intent, PendingIntentFlags.updateCurrent())
} catch (e: SecurityException) {
Log.w(NotificationFactory.TAG, "TaskStackBuilder too many pending intents device quirk: ${e.message}")
null
}
}
@@ -159,28 +162,28 @@ data class NotificationConversation(
.putParcelableArrayListExtra(DeleteNotificationReceiver.EXTRA_THREADS, arrayListOf(thread))
.makeUniqueToPreventMerging()
return PendingIntent.getBroadcast(context, 0, intent, PendingIntentFlags.updateCurrent())
return NotificationPendingIntentHelper.getBroadcast(context, 0, intent, PendingIntentFlags.updateCurrent())
}
fun getMarkAsReadIntent(context: Context): PendingIntent {
fun getMarkAsReadIntent(context: Context): PendingIntent? {
val intent = Intent(context, MarkReadReceiver::class.java)
.setAction(MarkReadReceiver.CLEAR_ACTION)
.putParcelableArrayListExtra(MarkReadReceiver.THREADS_EXTRA, arrayListOf(mostRecentNotification.thread))
.putExtra(MarkReadReceiver.NOTIFICATION_ID_EXTRA, notificationId)
.makeUniqueToPreventMerging()
return PendingIntent.getBroadcast(context, (thread.threadId * 2).toInt(), intent, PendingIntentFlags.updateCurrent())
return NotificationPendingIntentHelper.getBroadcast(context, (thread.threadId * 2).toInt(), intent, PendingIntentFlags.updateCurrent())
}
fun getQuickReplyIntent(context: Context): PendingIntent {
fun getQuickReplyIntent(context: Context): PendingIntent? {
val intent: Intent = ConversationIntents.createPopUpBuilder(context, recipient.id, mostRecentNotification.thread.threadId)
.build()
.makeUniqueToPreventMerging()
return PendingIntent.getActivity(context, (thread.threadId * 2).toInt() + 1, intent, PendingIntentFlags.updateCurrent())
return NotificationPendingIntentHelper.getActivity(context, (thread.threadId * 2).toInt() + 1, intent, PendingIntentFlags.updateCurrent())
}
fun getRemoteReplyIntent(context: Context, replyMethod: ReplyMethod): PendingIntent {
fun getRemoteReplyIntent(context: Context, replyMethod: ReplyMethod): PendingIntent? {
val intent = Intent(context, RemoteReplyReceiver::class.java)
.setAction(RemoteReplyReceiver.REPLY_ACTION)
.putExtra(RemoteReplyReceiver.RECIPIENT_EXTRA, recipient.id)
@@ -189,11 +192,11 @@ data class NotificationConversation(
.putExtra(RemoteReplyReceiver.GROUP_STORY_ID_EXTRA, notificationItems.first().thread.groupStoryId ?: Long.MIN_VALUE)
.makeUniqueToPreventMerging()
return PendingIntent.getBroadcast(context, (thread.threadId * 2).toInt() + 1, intent, PendingIntentFlags.updateCurrent())
return NotificationPendingIntentHelper.getBroadcast(context, (thread.threadId * 2).toInt() + 1, intent, PendingIntentFlags.updateCurrent())
}
fun getTurnOffJoinedNotificationsIntent(context: Context): PendingIntent {
return PendingIntent.getActivity(
fun getTurnOffJoinedNotificationsIntent(context: Context): PendingIntent? {
return NotificationPendingIntentHelper.getActivity(
context,
0,
TurnOffContactJoinedNotificationsActivity.newIntent(context, thread.threadId),

View File

@@ -2,7 +2,6 @@ package org.thoughtcrime.securesms.notifications.v2
import android.annotation.TargetApi
import android.app.Notification
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.graphics.BitmapFactory
@@ -240,7 +239,7 @@ object NotificationFactory {
setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_CHILDREN)
setChannelId(NotificationChannels.getMessagesChannel(context))
setContentTitle(context.getString(R.string.app_name))
setContentIntent(PendingIntent.getActivity(context, 0, MainActivity.clearTop(context), PendingIntentFlags.mutable()))
setContentIntent(NotificationPendingIntentHelper.getActivity(context, 0, MainActivity.clearTop(context), PendingIntentFlags.mutable()))
setGroupSummary(true)
setSubText(context.getString(R.string.MessageNotifier_d_new_messages_in_d_conversations, state.messageCount, state.threadCount))
setContentInfo(state.messageCount.toString())
@@ -321,7 +320,7 @@ object NotificationFactory {
setContentTitle(context.getString(R.string.MessageNotifier_message_delivery_failed))
setContentText(context.getString(R.string.MessageNotifier_failed_to_deliver_message))
setTicker(context.getString(R.string.MessageNotifier_error_delivering_message))
setContentIntent(PendingIntent.getActivity(context, 0, intent, PendingIntentFlags.mutable()))
setContentIntent(NotificationPendingIntentHelper.getActivity(context, 0, intent, PendingIntentFlags.mutable()))
setAutoCancel(true)
setAlarms(recipient)
setChannelId(NotificationChannels.FAILURES)
@@ -350,7 +349,7 @@ object NotificationFactory {
setLargeIcon(BitmapFactory.decodeResource(context.resources, R.drawable.ic_info_outline))
setContentTitle(context.getString(R.string.MessageNotifier_message_delivery_paused))
setContentText(context.getString(R.string.MessageNotifier_verify_to_continue_messaging_on_signal))
setContentIntent(PendingIntent.getActivity(context, 0, intent, PendingIntentFlags.mutable()))
setContentIntent(NotificationPendingIntentHelper.getActivity(context, 0, intent, PendingIntentFlags.mutable()))
setOnlyAlertOnce(true)
setAutoCancel(true)
setAlarms(recipient)

View File

@@ -0,0 +1,31 @@
package org.thoughtcrime.securesms.notifications.v2
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import org.signal.core.util.logging.Log
/**
* Wrapper for creating pending intents that catches security exceptions thrown by Xiaomi devices randomly.
*/
object NotificationPendingIntentHelper {
private val TAG = Log.tag(NotificationPendingIntentHelper::class.java)
fun getBroadcast(context: Context, requestCode: Int, intent: Intent, flags: Int): PendingIntent? {
return try {
PendingIntent.getBroadcast(context, requestCode, intent, flags)
} catch (e: SecurityException) {
Log.w(TAG, "Too many pending intents device quirk: ${e.message}")
null
}
}
fun getActivity(context: Context, requestCode: Int, intent: Intent, flags: Int): PendingIntent? {
return try {
PendingIntent.getActivity(context, requestCode, intent, flags)
} catch (e: SecurityException) {
Log.w(TAG, "Too many pending intents device quirk: ${e.message}")
null
}
}
}

View File

@@ -68,7 +68,7 @@ data class NotificationState(val conversations: List<NotificationConversation>,
.putParcelableArrayListExtra(DeleteNotificationReceiver.EXTRA_THREADS, ArrayList(threads))
.makeUniqueToPreventMerging()
return PendingIntent.getBroadcast(context, 0, intent, PendingIntentFlags.updateCurrent())
return NotificationPendingIntentHelper.getBroadcast(context, 0, intent, PendingIntentFlags.updateCurrent())
}
fun getMarkAsReadIntent(context: Context): PendingIntent? {
@@ -77,7 +77,7 @@ data class NotificationState(val conversations: List<NotificationConversation>,
.putExtra(MarkReadReceiver.NOTIFICATION_ID_EXTRA, NotificationIds.MESSAGE_SUMMARY)
.makeUniqueToPreventMerging()
return PendingIntent.getBroadcast(context, 0, intent, PendingIntentFlags.updateCurrent())
return NotificationPendingIntentHelper.getBroadcast(context, 0, intent, PendingIntentFlags.updateCurrent())
}
fun getThreadsWithMostRecentNotificationFromSelf(): Set<ConversationId> {