java.lang.IllegalArgumentException: Targeting S+ (version 31 and above) requires that one of FLAG_IMMUTABLE or FLAG_MUTABLE be specified when creating a PendingIntent signifie qu’une application Android ciblant le niveau d’API 31 ou supérieur a créé un PendingIntent sans déclarer sa mutabilité. Cette exception se produit à l’exécution, lors de la création du PendingIntent. La première mesure la plus sûre consiste à examiner la trace de pile et à repérer l’appel de fabrique exact avant de modifier les indicateurs. Si l’appel appartient au code de l’application et qu’aucun renseignement ultérieur des champs n’est nécessaire, conservez les indicateurs existants et ajoutez FLAG_IMMUTABLE.

Signification de l’erreur PendingIntent « Targeting S+ »

L’exigence de mutabilité des PendingIntent d’Android s’applique lorsqu’une application ciblant Android 12, soit le niveau d’API 31, ou une version ultérieure appelle PendingIntent.getActivity(), getActivities(), getBroadcast(), getService() ou getForegroundService() sans fournir l’un des deux indicateurs de mutabilité explicites.

FLAG_IMMUTABLE et FLAG_MUTABLE sont deux possibilités exclusives : ne les associez pas. Android recommande d’utiliser des PendingIntent non modifiables dans la plupart des cas. Un PendingIntent modifiable ne convient que lorsqu’un destinataire ou une API Android doit renseigner des champs encore vides dans l’Intent encapsulé. La documentation de référence de l’API PendingIntent distingue également ces déclarations de mutabilité des indicateurs de comportement tels que FLAG_UPDATE_CURRENT, FLAG_CANCEL_CURRENT et FLAG_ONE_SHOT.

Cette exception d’exécution est différente de l’exigence android:exported d’Android 12, qui concerne les déclarations de composants dans le manifeste.

Repérer l’appel PendingIntent qui déclenche réellement l’exception

Le message d’exception signale l’absence de déclaration, mais n’indique pas si l’appel appartient au code de l’application, à du code généré ou à une bibliothèque intégrée.

  1. Partez de java.lang.IllegalArgumentException dans la trace de pile complète.
  2. Repérez la première entrée pertinente hors du framework associée à l’une des méthodes de fabrique de PendingIntent.
  3. Rattachez cette entrée à son paquet, à son module, à son composant généré ou à sa dépendance intégrée.
  4. Notez la méthode de fabrique, le code de requête, l’Intent encapsulé et l’expression complète des indicateurs.
  5. Déterminez si l’appel appartient à l’application, à du code généré ou à une dépendance avant de choisir une correction.

Ne modifiez pas un PendingIntent sans rapport avec l’exception au seul motif qu’il apparaît dans le code de l’application. Si l’entrée concernée appartient à une dépendance, modifier un indicateur ailleurs dans l’application peut laisser intacte l’implémentation défaillante.

Corriger le code de l’application avec FLAG_IMMUTABLE par défaut

Quand utiliser cette solution : suivez cette procédure lorsque l’appel de fabrique appartient au code de l’application et qu’aucun expéditeur, destinataire ou aucune API Android ne doit modifier les champs non renseignés de l’Intent encapsulé.

Conditions préalables : repérez l’appel exact et conservez son code de requête, son Intent, sa méthode de fabrique et ses indicateurs de comportement. L’ajout de FLAG_IMMUTABLE ne doit pas supprimer FLAG_UPDATE_CURRENT, FLAG_CANCEL_CURRENT, FLAG_ONE_SHOT ni aucun autre indicateur existant nécessaire.

  1. Conservez le code de requête et l’Intent actuels sans les modifier.
  2. Associez FLAG_IMMUTABLE aux indicateurs existants avec or en Kotlin ou | en Java.
  3. Créez le PendingIntent avec l’expression combinée.
  4. Reproduisez l’action qui envoie ce PendingIntent.

Exemple en Kotlin

Avant :

PendingIntent.getActivity(context, existingRequestCode, existingIntent, existingFlags)

Après :

PendingIntent.getActivity(context, existingRequestCode, existingIntent, existingFlags or PendingIntent.FLAG_IMMUTABLE)

Exemple en Java

Avant :

PendingIntent.getActivity(context, existingRequestCode, existingIntent, existingFlags)

Après :

PendingIntent.getActivity(context, existingRequestCode, existingIntent, existingFlags | PendingIntent.FLAG_IMMUTABLE)

Résultat attendu : l’appel de fabrique fournit désormais la déclaration de mutabilité requise. Vous devez encore tester l’action de notification, l’ouverture d’activité, l’alarme, le widget, le rappel ou le démarrage de service concerné afin de vérifier que son comportement reste inchangé. Le créateur d’un PendingIntent non modifiable peut toujours le mettre à jour avec FLAG_UPDATE_CURRENT.

Risque et retour arrière : cette modification de code présente un faible risque et aucun risque documenté de perte de données. Si les tests montrent que le destinataire ou l’API de la plateforme doit renseigner certains champs, retirez uniquement la déclaration d’immuabilité ajoutée, puis évaluez la solution ciblée avec FLAG_MUTABLE ci-dessous.

Utiliser FLAG_MUTABLE uniquement si la fonctionnalité doit renseigner des champs

Quand utiliser cette solution : utilisez FLAG_MUTABLE uniquement lorsqu’une fonctionnalité documentée doit modifier l’Intent encapsulé, notamment pour les réponses intégrées, les bulles, les rappels de localisation, les données supplémentaires de comptage des alarmes répétitives ou une autre opération requise de renseignement des champs.

Besoin Déclaration
Aucune modification par un destinataire ou par la plateforme n’est nécessaire FLAG_IMMUTABLE
Une fonctionnalité documentée doit fournir des données supplémentaires FLAG_MUTABLE

Conditions préalables : vérifiez le besoin de modification pour la fonctionnalité concernée. Conservez tous les indicateurs de comportement nécessaires et utilisez, lorsque c’est possible, un Intent de base explicite dont l’action, le paquet et le composant sont définis.

  1. Conservez le code de requête, le type de fabrique et les indicateurs de comportement nécessaires.
  2. Ajoutez FLAG_MUTABLE à la place de FLAG_IMMUTABLE.
  3. Rendez l’Intent de base explicite lorsque c’est possible.
  4. Testez de nouveau l’opération exacte qui nécessite le renseignement de champs.
  5. Confirmez que seuls les champs documentés doivent rester modifiables.

Exemple en Kotlin

PendingIntent.getActivity(context, existingRequestCode, explicitIntent, existingFlags or PendingIntent.FLAG_MUTABLE)

Exemple en Java

PendingIntent.getActivity(context, existingRequestCode, explicitIntent, existingFlags | PendingIntent.FLAG_MUTABLE)

Résultat attendu : l’appel de fabrique déclare la mutabilité tout en conservant les indicateurs de comportement existants, et l’opération documentée de renseignement par le destinataire ou la plateforme reste disponible.

Risque et retour arrière : il s’agit d’une décision de sécurité présentant un risque moyen, mais aucun risque documenté de perte de données. Un PendingIntent modifiable délègue le contrôle des champs non renseignés de l’Intent ; une utilisation trop large peut donc accroître l’exposition. Suivez les recommandations de sécurité Android concernant les PendingIntent, privilégiez un Intent de base explicite et remplacez FLAG_MUTABLE par FLAG_IMMUTABLE si le besoin de renseignement disparaît. Le même principe de moindre privilège s’applique lors de l’examen du partage sécurisé d’URI entre applications Android.

Traiter un PendingIntent créé par une dépendance

Si la première entrée pertinente de la trace de pile appartient à du code généré ou à un SDK intégré, modifier un PendingIntent sans rapport appartenant à l’application ne constitue pas une correction confirmée. La correction d’une dépendance dépend du projet et doit être vérifiée dans la documentation principale de l’artefact concerné.

  1. À partir du paquet ou du module indiqué dans la trace de pile, identifiez précisément l’artefact intégré qui possède l’appel.
  2. Consultez la documentation principale des versions de cet artefact afin de trouver une version maintenue qui corrige explicitement la compatibilité des PendingIntent avec Android 12.
  3. Si une telle version est documentée, effectuez la mise à jour uniquement selon la procédure de gestion des dépendances déjà établie dans le projet.
  4. Examinez le graphe des dépendances résolues pour confirmer que l’artefact prévu a été sélectionné, et non une ancienne version transitive.
  5. Recompilez l’application et reproduisez la même fonctionnalité.
  6. Confirmez que l’implémentation corrigée est incluse dans l’application et que l’appel d’origine dépourvu de déclaration de mutabilité n’apparaît plus.

Résultat attendu : si la version vérifiée de la dépendance contient la correction et que l’application recompilée résout bien cette version, l’appel de fabrique appartenant à la dépendance devrait fournir une déclaration de mutabilité explicite. Ce résultat ne peut pas être déduit de la seule version demandée dans la configuration.

Risque et retour arrière : la modification d’une dépendance peut changer le comportement d’autres fonctionnalités. Consultez les informations de version de la dépendance et rétablissez l’état antérieur des dépendances du projet si la mise à jour provoque une régression. Aucun nom de dépendance ni aucune version corrigée universelle ne sont établis pour cette exception.

Utiliser PendingIntentCompat lorsque la gestion de la compatibilité est adaptée

Quand utiliser cette solution : utilisez PendingIntentCompat d’AndroidX lorsque le code de l’application prend en charge des versions de la plateforme pour lesquelles cet utilitaire de compatibilité est préférable et que l’appelant peut toujours indiquer si le PendingIntent doit être modifiable.

Conditions préalables : confirmez que la version d’AndroidX Core utilisée par le projet contient PendingIntentCompat, repérez le type de fabrique d’origine et déterminez la mutabilité selon les besoins réels de la fonctionnalité.

  1. Choisissez la méthode correspondant à l’opération d’origine : getActivity, getBroadcast, getService ou getForegroundService.
  2. Transmettez à cette méthode les indicateurs de comportement existants qui ne concernent pas la mutabilité.
  3. Définissez isMutable sur false, sauf si un besoin de modification vérifié s’applique.
  4. Conservez le code de requête et le comportement de l’Intent encapsulé.
  5. Effectuez les tests avec la configuration concernée ciblant Android 12 ou une version ultérieure.

Résultat attendu : l’utilitaire de compatibilité associe la décision explicite de l’appelant concernant la mutabilité aux indicateurs existants, selon les besoins des versions de plateforme prises en charge.

Risque et retour arrière : cette solution de compatibilité présente un faible risque et aucun risque documenté de perte de données, mais le remplacement des API utilitaires peut modifier la structure de l’appel. Si ce changement provoque une régression, revenez à la méthode de fabrique correspondante du framework tout en conservant un indicateur de mutabilité explicite.

Vérifier la correction sans modifier le comportement du PendingIntent

  1. Exécutez l’application recompilée avec un SDK cible de niveau d’API 31 ou supérieur, puis reproduisez le déclencheur d’origine.
  2. Confirmez que l’exception exacte Targeting S+ ne se produit plus au niveau de l’appel de fabrique identifié.
  3. Testez de nouveau l’appui ou l’action de notification, l’alarme, le widget, le rappel, l’ouverture d’activité ou le démarrage de service concerné.
  4. Si FLAG_UPDATE_CURRENT, FLAG_CANCEL_CURRENT ou FLAG_ONE_SHOT était présent, vérifiez que le comportement d’origine de mise à jour, d’annulation ou d’exécution unique est conservé.
  5. Pour un PendingIntent modifiable, vérifiez uniquement le renseignement nécessaire et confirmez que l’Intent de base est explicite lorsque c’est possible.
  6. Pour la correction d’une dépendance, confirmez que l’artefact résolu et intégré correspond à la version vérifiée, sans vous fier uniquement à la déclaration de dépendance demandée.

Une compilation réussie ne prouve pas à elle seule que le chemin d’exécution est corrigé. Distinguez également les restrictions d’Android 14/API 34 concernant les PendingIntent implicites modifiables : elles produisent une autre erreur et ne sont pas corrigées par cette procédure destinée au niveau d’API 31.