Did you ever notice?
- Sometimes tapping a notification 🔔 and nothing happens for a couple of minutes, then it suddenly pops up? 😬
Starting with Android 12 notifications will not work if they do not start activities directly.
What is a Notification trampoline?
Some apps respond to notification taps by launching an app component (Service, BroadcastReceiver) that starts the activity that the user finally sees and interacts with. This app component is known as a notification trampoline.
Android12 restrictions
- Applications that target Android 12 or higher can’t start
activitiesfromservices or broadcast receiversthat are used as notification trampolines.
Or
- In simple words after the user taps on a notification or an action button within the notification, the app cannot
call startActivity()inside of aservice or broadcast receiverwhich helps to improve the app performance and user experience.
Error message in logcat
The system prevents the activity from starting when an application tries to start an activity from a service or broadcast receiver that acts as a notification trampoline, and the following message appears in Logcat:
| Indirect notification activity start (trampoline) from PACKAGE_NAME, \ | |
| this should be avoided for performance reasons. |
Sample code that will create this issue
- Trampoline component: Here we have a
BroadcastReceiverthat willstart an activitywhen the user taps on the notification action.
| class NotificationReceiver : BroadcastReceiver() { | |
| override fun onReceive(context: Context, intent: Intent) { | |
| // from Android12 this will not work | |
| // Indirect notification activity start (trampoline) from PACKAGE_NAME, \ this should be | |
| //avoided for performance reasons. | |
| context.startActivity(Intent(context, NotificationTrampolineActivity::class.java).apply { | |
| flags = Intent.FLAG_ACTIVITY_NEW_TASK | |
| }) | |
| } | |
| } |
| val broadcastIntent = Intent(context.applicationContext, NotificationReceiver::class.java) | |
| val actionIntent = PendingIntent.getBroadcast( | |
| context.applicationContext, | |
| 0, broadcastIntent, PendingIntent.FLAG_UPDATE_CURRENT or | |
| // mutability flag required when targeting Android12 or higher | |
| PendingIntent.FLAG_IMMUTABLE | |
| ) | |
| // notification code | |
| val notification = NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID) | |
| .setSmallIcon(R.mipmap.ic_launcher) | |
| .setContentTitle("Android12") | |
| .setContentText("Notification trampoline restrictions") | |
| .addAction(R.mipmap.ic_launcher, "Open activity from receiver", actionIntent) | |
| .setAutoCancel(true) | |
| .build() | |
| notificationManager.notify(getUniqueId(), notification) |
NotificationWithReceiverIntent.kt
Identify which app components act as notification trampolines
- During the testing of the application, after you tap on a notification, you can identify which service or broadcast receiver acted as the notification trampoline in your app.
- To do so, look at the output of the following terminal command:
| adb shell dumpsys activity service \ com.android.systemui/.dump.SystemUIAuxiliaryDumpService | |
| A section of the output includes the text "NotifInteractionLog". | |
| This section contains the information that's necessary to identify the component that starts as the result of a notification tap. |
Job Offers
Toggle the behavior
During the testing of a debuggable version of the application, we can enable and disable this restriction using the NOTIFICATION_TRAMPOLINE_BLOCK app compatibility flag.
You can find it under the developer options
App compatibility flag
Update your app
- If you find that your application starts an
activity from a service or broadcast receiverthat acts as a notification trampoline, complete the following migration steps :
- Create a
PendingIntentobject that is associated with theactivitythat users see after they tap on the notification. - Use the
PendingIntentobject which is created in the previous step as part of building your notification.
| val notificationTrampolineActivityIntent = | |
| Intent(context.applicationContext, NotificationTrampolineActivity::class.java) | |
| // Create the TaskStackBuilder | |
| val resultPendingIntent: PendingIntent? = TaskStackBuilder.create(context).run { | |
| // Add the intent, which inflates the back stack | |
| addNextIntentWithParentStack(notificationTrampolineActivityIntent) | |
| // Get the PendingIntent containing the entire back stack | |
| getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT or | |
| // mutability flag required when targeting Android12 or higher | |
| PendingIntent.FLAG_IMMUTABLE) | |
| val notification = NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID) | |
| .setSmallIcon(R.mipmap.ic_launcher) | |
| .setContentTitle("Android12") | |
| .setContentText("Notification trampoline restrictions fix") | |
| .addAction(R.mipmap.ic_launcher, "Open activity", resultPendingIntent) | |
| .setAutoCancel(true) | |
| .build() | |
| notificationManager.notify(getUniqueId(), notification) |



