From f88a477f0479447b6024fd9b2df891781cdba2f9 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 9 Nov 2021 20:02:02 +0100 Subject: [PATCH] Fix crash on Android 12: PendingIntent.FLAG_IMMUTABLE has to be set --- .../receiver/AlarmSyncBroadcastReceiver.kt | 15 ++- .../app/core/platform/PendingIntentCompat.kt | 34 +++++ .../app/core/services/VectorSyncService.kt | 5 +- .../notifications/NotificationUtils.kt | 123 +++++++++++++----- 4 files changed, 138 insertions(+), 39 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/core/platform/PendingIntentCompat.kt diff --git a/vector/src/fdroid/java/im/vector/app/fdroid/receiver/AlarmSyncBroadcastReceiver.kt b/vector/src/fdroid/java/im/vector/app/fdroid/receiver/AlarmSyncBroadcastReceiver.kt index 0f375561b2..c1fda2d404 100644 --- a/vector/src/fdroid/java/im/vector/app/fdroid/receiver/AlarmSyncBroadcastReceiver.kt +++ b/vector/src/fdroid/java/im/vector/app/fdroid/receiver/AlarmSyncBroadcastReceiver.kt @@ -25,6 +25,7 @@ import android.os.Build import androidx.core.content.ContextCompat import androidx.core.content.getSystemService import im.vector.app.core.extensions.singletonEntryPoint +import im.vector.app.core.platform.PendingIntentCompat import im.vector.app.core.services.VectorSyncService import org.matrix.android.sdk.internal.session.sync.job.SyncService import timber.log.Timber @@ -67,7 +68,12 @@ class AlarmSyncBroadcastReceiver : BroadcastReceiver() { putExtra(SyncService.EXTRA_SESSION_ID, sessionId) putExtra(SyncService.EXTRA_PERIODIC, true) } - val pIntent = PendingIntent.getBroadcast(context, REQUEST_CODE, intent, PendingIntent.FLAG_UPDATE_CURRENT) + val pIntent = PendingIntent.getBroadcast( + context, + REQUEST_CODE, + intent, + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE + ) val firstMillis = System.currentTimeMillis() + delayInSeconds * 1000L val alarmMgr = context.getSystemService()!! if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { @@ -80,7 +86,12 @@ class AlarmSyncBroadcastReceiver : BroadcastReceiver() { fun cancelAlarm(context: Context) { Timber.v("## Sync: Cancel alarm for background sync") val intent = Intent(context, AlarmSyncBroadcastReceiver::class.java) - val pIntent = PendingIntent.getBroadcast(context, REQUEST_CODE, intent, PendingIntent.FLAG_UPDATE_CURRENT) + val pIntent = PendingIntent.getBroadcast( + context, + REQUEST_CODE, + intent, + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE + ) val alarmMgr = context.getSystemService()!! alarmMgr.cancel(pIntent) diff --git a/vector/src/main/java/im/vector/app/core/platform/PendingIntentCompat.kt b/vector/src/main/java/im/vector/app/core/platform/PendingIntentCompat.kt new file mode 100644 index 0000000000..832c11888c --- /dev/null +++ b/vector/src/main/java/im/vector/app/core/platform/PendingIntentCompat.kt @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.core.platform + +import android.app.PendingIntent +import android.os.Build + +object PendingIntentCompat { + val FLAG_IMMUTABLE = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + PendingIntent.FLAG_IMMUTABLE + } else { + 0 + } + + val FLAG_MUTABLE = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + PendingIntent.FLAG_MUTABLE + } else { + 0 + } +} diff --git a/vector/src/main/java/im/vector/app/core/services/VectorSyncService.kt b/vector/src/main/java/im/vector/app/core/services/VectorSyncService.kt index 08db8227a6..a6f6d8d82f 100644 --- a/vector/src/main/java/im/vector/app/core/services/VectorSyncService.kt +++ b/vector/src/main/java/im/vector/app/core/services/VectorSyncService.kt @@ -32,6 +32,7 @@ import androidx.work.Worker import androidx.work.WorkerParameters import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R +import im.vector.app.core.platform.PendingIntentCompat import im.vector.app.features.notifications.NotificationUtils import im.vector.app.features.settings.BackgroundSyncMode import org.matrix.android.sdk.internal.session.sync.job.SyncService @@ -199,9 +200,9 @@ private fun Context.rescheduleSyncService(sessionId: String, startService(intent) } else { val pendingIntent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - PendingIntent.getForegroundService(this, 0, intent, 0) + PendingIntent.getForegroundService(this, 0, intent, PendingIntentCompat.FLAG_IMMUTABLE) } else { - PendingIntent.getService(this, 0, intent, 0) + PendingIntent.getService(this, 0, intent, PendingIntentCompat.FLAG_IMMUTABLE) } val firstMillis = System.currentTimeMillis() + syncDelaySeconds * 1000L val alarmMgr = getSystemService()!! diff --git a/vector/src/main/java/im/vector/app/features/notifications/NotificationUtils.kt b/vector/src/main/java/im/vector/app/features/notifications/NotificationUtils.kt index 491302a225..650035609d 100755 --- a/vector/src/main/java/im/vector/app/features/notifications/NotificationUtils.kt +++ b/vector/src/main/java/im/vector/app/features/notifications/NotificationUtils.kt @@ -48,6 +48,7 @@ import androidx.fragment.app.Fragment import im.vector.app.BuildConfig import im.vector.app.R import im.vector.app.core.extensions.createIgnoredUri +import im.vector.app.core.platform.PendingIntentCompat import im.vector.app.core.resources.StringProvider import im.vector.app.core.services.CallService import im.vector.app.core.utils.startNotificationChannelSettingsIntent @@ -227,7 +228,7 @@ class NotificationUtils @Inject constructor(private val context: Context, // build the pending intent go to the home screen if this is clicked. val i = HomeActivity.newIntent(context) i.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP - val pi = PendingIntent.getActivity(context, 0, i, 0) + val pi = PendingIntent.getActivity(context, 0, i, PendingIntentCompat.FLAG_IMMUTABLE) val accentColor = ContextCompat.getColor(context, R.color.notification_accent_color) @@ -320,16 +321,23 @@ class NotificationUtils @Inject constructor(private val context: Context, flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP data = createIgnoredUri(call.callId) } - val contentPendingIntent = PendingIntent.getActivity(context, System.currentTimeMillis().toInt(), contentIntent, 0) + val contentPendingIntent = PendingIntent.getActivity( + context, + System.currentTimeMillis().toInt(), + contentIntent, + PendingIntentCompat.FLAG_IMMUTABLE + ) val answerCallPendingIntent = TaskStackBuilder.create(context) .addNextIntentWithParentStack(HomeActivity.newIntent(context)) - .addNextIntent(VectorCallActivity.newIntent( - context = context, - call = call, - mode = VectorCallActivity.INCOMING_ACCEPT) + .addNextIntent( + VectorCallActivity.newIntent( + context = context, + call = call, + mode = VectorCallActivity.INCOMING_ACCEPT + ) ) - .getPendingIntent(System.currentTimeMillis().toInt(), PendingIntent.FLAG_UPDATE_CURRENT) + .getPendingIntent(System.currentTimeMillis().toInt(), PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE) val rejectCallPendingIntent = buildRejectCallPendingIntent(call.callId) @@ -338,7 +346,8 @@ class NotificationUtils @Inject constructor(private val context: Context, IconCompat.createWithResource(context, R.drawable.ic_call_hangup) .setTint(ThemeUtils.getColor(context, R.attr.colorError)), getActionText(R.string.call_notification_reject, R.attr.colorError), - rejectCallPendingIntent) + rejectCallPendingIntent + ) ) builder.addAction( @@ -381,7 +390,12 @@ class NotificationUtils @Inject constructor(private val context: Context, flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP data = createIgnoredUri(call.callId) } - val contentPendingIntent = PendingIntent.getActivity(context, System.currentTimeMillis().toInt(), contentIntent, 0) + val contentPendingIntent = PendingIntent.getActivity( + context, + System.currentTimeMillis().toInt(), + contentIntent, + PendingIntentCompat.FLAG_IMMUTABLE + ) val rejectCallPendingIntent = buildRejectCallPendingIntent(call.callId) @@ -390,7 +404,8 @@ class NotificationUtils @Inject constructor(private val context: Context, IconCompat.createWithResource(context, R.drawable.ic_call_hangup) .setTint(ThemeUtils.getColor(context, R.attr.colorError)), getActionText(R.string.call_notification_hangup, R.attr.colorError), - rejectCallPendingIntent) + rejectCallPendingIntent + ) ) builder.setContentIntent(contentPendingIntent) @@ -431,13 +446,14 @@ class NotificationUtils @Inject constructor(private val context: Context, IconCompat.createWithResource(context, R.drawable.ic_call_hangup) .setTint(ThemeUtils.getColor(context, R.attr.colorError)), getActionText(R.string.call_notification_hangup, R.attr.colorError), - rejectCallPendingIntent) + rejectCallPendingIntent + ) ) val contentPendingIntent = TaskStackBuilder.create(context) .addNextIntentWithParentStack(HomeActivity.newIntent(context)) .addNextIntent(VectorCallActivity.newIntent(context, call, null)) - .getPendingIntent(System.currentTimeMillis().toInt(), PendingIntent.FLAG_UPDATE_CURRENT) + .getPendingIntent(System.currentTimeMillis().toInt(), PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE) builder.setContentIntent(contentPendingIntent) @@ -453,7 +469,7 @@ class NotificationUtils @Inject constructor(private val context: Context, context, System.currentTimeMillis().toInt(), rejectCallActionReceiver, - PendingIntent.FLAG_UPDATE_CURRENT + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE ) } @@ -499,7 +515,7 @@ class NotificationUtils @Inject constructor(private val context: Context, val contentPendingIntent = TaskStackBuilder.create(context) .addNextIntentWithParentStack(HomeActivity.newIntent(context)) .addNextIntent(RoomDetailActivity.newIntent(context, RoomDetailArgs(callInformation.nativeRoomId))) - .getPendingIntent(System.currentTimeMillis().toInt(), PendingIntent.FLAG_UPDATE_CURRENT) + .getPendingIntent(System.currentTimeMillis().toInt(), PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE) builder.setContentIntent(contentPendingIntent) return builder.build() @@ -517,7 +533,10 @@ class NotificationUtils @Inject constructor(private val context: Context, addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) } PendingIntent.getActivity( - context, System.currentTimeMillis().toInt(), intent, PendingIntent.FLAG_UPDATE_CURRENT + context, + System.currentTimeMillis().toInt(), + intent, + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE ).let { setContentIntent(it) } @@ -587,8 +606,12 @@ class NotificationUtils @Inject constructor(private val context: Context, markRoomReadIntent.action = MARK_ROOM_READ_ACTION markRoomReadIntent.data = createIgnoredUri(roomInfo.roomId) markRoomReadIntent.putExtra(NotificationBroadcastReceiver.KEY_ROOM_ID, roomInfo.roomId) - val markRoomReadPendingIntent = PendingIntent.getBroadcast(context, System.currentTimeMillis().toInt(), markRoomReadIntent, - PendingIntent.FLAG_UPDATE_CURRENT) + val markRoomReadPendingIntent = PendingIntent.getBroadcast( + context, + System.currentTimeMillis().toInt(), + markRoomReadIntent, + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE + ) NotificationCompat.Action.Builder(R.drawable.ic_material_done_all_white, stringProvider.getString(R.string.action_mark_room_read), markRoomReadPendingIntent) @@ -624,8 +647,12 @@ class NotificationUtils @Inject constructor(private val context: Context, val intent = Intent(context, NotificationBroadcastReceiver::class.java) intent.putExtra(NotificationBroadcastReceiver.KEY_ROOM_ID, roomInfo.roomId) intent.action = DISMISS_ROOM_NOTIF_ACTION - val pendingIntent = PendingIntent.getBroadcast(context.applicationContext, - System.currentTimeMillis().toInt(), intent, PendingIntent.FLAG_UPDATE_CURRENT) + val pendingIntent = PendingIntent.getBroadcast( + context.applicationContext, + System.currentTimeMillis().toInt(), + intent, + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE + ) setDeleteIntent(pendingIntent) } .setTicker(tickerText) @@ -655,31 +682,41 @@ class NotificationUtils @Inject constructor(private val context: Context, rejectIntent.action = REJECT_ACTION rejectIntent.data = createIgnoredUri("$roomId&$matrixId") rejectIntent.putExtra(NotificationBroadcastReceiver.KEY_ROOM_ID, roomId) - val rejectIntentPendingIntent = PendingIntent.getBroadcast(context, System.currentTimeMillis().toInt(), rejectIntent, - PendingIntent.FLAG_UPDATE_CURRENT) + val rejectIntentPendingIntent = PendingIntent.getBroadcast( + context, + System.currentTimeMillis().toInt(), + rejectIntent, + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE + ) addAction( R.drawable.vector_notification_reject_invitation, stringProvider.getString(R.string.reject), - rejectIntentPendingIntent) + rejectIntentPendingIntent + ) // offer to type a quick accept button val joinIntent = Intent(context, NotificationBroadcastReceiver::class.java) joinIntent.action = JOIN_ACTION joinIntent.data = createIgnoredUri("$roomId&$matrixId") joinIntent.putExtra(NotificationBroadcastReceiver.KEY_ROOM_ID, roomId) - val joinIntentPendingIntent = PendingIntent.getBroadcast(context, System.currentTimeMillis().toInt(), joinIntent, - PendingIntent.FLAG_UPDATE_CURRENT) + val joinIntentPendingIntent = PendingIntent.getBroadcast( + context, + System.currentTimeMillis().toInt(), + joinIntent, + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE + ) addAction( R.drawable.vector_notification_accept_invitation, stringProvider.getString(R.string.join), - joinIntentPendingIntent) + joinIntentPendingIntent + ) val contentIntent = HomeActivity.newIntent(context, inviteNotificationRoomId = inviteNotifiableEvent.roomId) contentIntent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP // pending intent get reused by system, this will mess up the extra params, so put unique info to avoid that contentIntent.data = createIgnoredUri(inviteNotifiableEvent.eventId) - setContentIntent(PendingIntent.getActivity(context, 0, contentIntent, 0)) + setContentIntent(PendingIntent.getActivity(context, 0, contentIntent, PendingIntentCompat.FLAG_IMMUTABLE)) if (inviteNotifiableEvent.noisy) { // Compat @@ -718,7 +755,7 @@ class NotificationUtils @Inject constructor(private val context: Context, contentIntent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP // pending intent get reused by system, this will mess up the extra params, so put unique info to avoid that contentIntent.data = createIgnoredUri(simpleNotifiableEvent.eventId) - setContentIntent(PendingIntent.getActivity(context, 0, contentIntent, 0)) + setContentIntent(PendingIntent.getActivity(context, 0, contentIntent, PendingIntentCompat.FLAG_IMMUTABLE)) if (simpleNotifiableEvent.noisy) { // Compat @@ -745,14 +782,22 @@ class NotificationUtils @Inject constructor(private val context: Context, return TaskStackBuilder.create(context) .addNextIntentWithParentStack(HomeActivity.newIntent(context)) .addNextIntent(roomIntentTap) - .getPendingIntent(System.currentTimeMillis().toInt(), PendingIntent.FLAG_UPDATE_CURRENT) + .getPendingIntent( + System.currentTimeMillis().toInt(), + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE + ) } private fun buildOpenHomePendingIntentForSummary(): PendingIntent { val intent = HomeActivity.newIntent(context, clearNotification = true) intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP intent.data = createIgnoredUri("tapSummary") - return PendingIntent.getActivity(context, Random.nextInt(1000), intent, PendingIntent.FLAG_UPDATE_CURRENT) + return PendingIntent.getActivity( + context, + Random.nextInt(1000), + intent, + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE + ) } /* @@ -769,8 +814,12 @@ class NotificationUtils @Inject constructor(private val context: Context, intent.action = SMART_REPLY_ACTION intent.data = createIgnoredUri(roomId) intent.putExtra(NotificationBroadcastReceiver.KEY_ROOM_ID, roomId) - return PendingIntent.getBroadcast(context, System.currentTimeMillis().toInt(), intent, - PendingIntent.FLAG_UPDATE_CURRENT) + return PendingIntent.getBroadcast( + context, + System.currentTimeMillis().toInt(), + intent, + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE + ) } else { /* TODO @@ -783,7 +832,7 @@ class NotificationUtils @Inject constructor(private val context: Context, // the action must be unique else the parameters are ignored quickReplyIntent.action = QUICK_LAUNCH_ACTION quickReplyIntent.data = createIgnoredUri($roomId") - return PendingIntent.getActivity(context, 0, quickReplyIntent, 0) + return PendingIntent.getActivity(context, 0, quickReplyIntent, PendingIntentCompat.FLAG_IMMUTABLE) } */ } @@ -837,8 +886,12 @@ class NotificationUtils @Inject constructor(private val context: Context, val intent = Intent(context, NotificationBroadcastReceiver::class.java) intent.action = DISMISS_SUMMARY_ACTION intent.data = createIgnoredUri("deleteSummary") - return PendingIntent.getBroadcast(context.applicationContext, - 0, intent, PendingIntent.FLAG_UPDATE_CURRENT) + return PendingIntent.getBroadcast( + context.applicationContext, + 0, + intent, + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE + ) } fun showNotificationMessage(tag: String?, id: Int, notification: Notification) { @@ -875,7 +928,7 @@ class NotificationUtils @Inject constructor(private val context: Context, context, 0, testActionIntent, - PendingIntent.FLAG_UPDATE_CURRENT + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE ) notificationManager.notify(