Comme les versions précédentes, Android 14 apporte des modifications de comportement pouvant affecter votre application. Les modifications de comportement suivantes s'appliquent exclusivement aux applications qui ciblent Android 14 (niveau d'API 34) ou version ultérieure. Si votre application cible Android 14 ou une version ultérieure, vous devez la modifier pour qu'elle prenne en charge ces comportements, le cas échéant.
Veillez également à consulter la liste des modifications de comportement qui affectent toutes les applications exécutées sur Android 14, peu importe la targetSdkVersion
de l'application.
Fonctionnalité de base
Le type de service au premier plan doit être indiqué
Si votre application cible Android 14 (niveau d'API 34) ou version ultérieure, elle doit spécifier au moins un type de service de premier plan pour chaque service de premier plan dans votre application. Vous devez choisir un type de service de premier plan qui représente le cas d'utilisation de votre application. Le système s'attend à ce que des services de premier plan présentant un type particulier répondent à un cas d'utilisation particulier.
Si un cas d'utilisation dans votre application n'est associé à aucun de ces types, il est fortement recommandé de migrer votre logique pour utiliser WorkManager ou les tâches de transfert de données déclenchées par l'utilisateur.
Application de l'autorisation BLUETOOTH_CONNECT dans BluetoothAdapter
Android 14 applique l'autorisation BLUETOOTH_CONNECT
lors de l'appel de la méthode BluetoothAdapter
getProfileConnectionState()
pour les applications ciblant Android 14 (niveau d'API 34) ou version ultérieure.
Cette méthode nécessitait déjà l'autorisation BLUETOOTH_CONNECT
, mais elle n'était pas appliquée. Assurez-vous que votre application déclare BLUETOOTH_CONNECT
dans le fichier AndroidManifest.xml
de votre application, comme indiqué dans l'extrait de code suivant, et vérifiez qu'un utilisateur a accordé l'autorisation avant d'appeler getProfileConnectionState
.
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
Mises à jour OpenJDK 17
Android 14 poursuit le travail d'actualisation des principales bibliothèques Android afin de s'adapter aux fonctionnalités des dernières versions d'OpenJDK LTS, y compris les mises à jour de la bibliothèque et la compatibilité avec le langage Java 17 pour les développeurs d'applications et de plates-formes.
Quelques modifications peuvent avoir un impact sur la compatibilité des applications :
- Modifications apportées aux expressions régulières : les références de groupe non valides sont désormais interdites afin de suivre de manière plus fidèle la sémantique d'OpenJDK. Vous pouvez rencontrer de nouveaux cas où une
IllegalArgumentException
est générée par la classejava.util.regex.Matcher
. Veillez donc à tester votre application pour les éléments qui utilisent des expressions régulières. Pour activer ou désactiver cette modification lors des tests, activez/désactivez l'indicateurDISALLOW_INVALID_GROUP_REFERENCE
à l'aide des outils du framework de compatibilité. - Gestion de l'UUID : la méthode
java.util.UUID.fromString()
effectue désormais des contrôles plus stricts lors de la validation de l'argument d'entrée. Vous pouvez donc voir uneIllegalArgumentException
pendant la désérialisation. Pour activer ou désactiver cette modification lors des tests, activez/désactivez l'indicateurENABLE_STRICT_VALIDATION
à l'aide des outils du framework de compatibilité. - Problèmes avec ProGuard : dans certains cas, l'ajout de la classe
java.lang.ClassValue
cause un problème si vous essayez de réduire, d'obscurcir ou d'optimiser votre application avec ProGuard. Le problème provient d'une bibliothèque Kotlin qui modifie le comportement d'exécution selon queClass.forName("java.lang.ClassValue")
renvoie une classe ou non. Si votre application a été développée avec une ancienne version de l'environnement d'exécution sans la classejava.lang.ClassValue
disponible, ces optimisations peuvent supprimer la méthodecomputeValue
des classes dérivées dejava.lang.ClassValue
.
JobScheduler renforce le comportement des rappels et du réseau
Depuis son lancement, JobScheduler s'attend à ce que votre application renvoie une valeur de onStartJob
ou onStopJob
dans les quelques secondes qui suivent. Avant Android 14,
Si une tâche s'exécute trop longtemps, elle est arrêtée et échoue en mode silencieux.
Si votre application cible Android 14 (niveau d'API 34) ou une version ultérieure et que
dépasse le temps accordé sur le thread principal, l'application déclenche une erreur ANR
avec le message d'erreur "Aucune réponse à onStartJob
" ou
"Aucune réponse à onStopJob
."
Cette erreur ANR peut être due à deux scénarios:
1. Des tâches bloquent le thread principal, empêchant les rappels onStartJob
ou onStopJob
de s'exécuter et de se terminer dans le délai prévu.
2. Le développeur exécute une tâche bloquante dans le rappel JobScheduler onStartJob
ou onStopJob
, ce qui empêche le rappel de se terminer dans le délai prévu.
Pour résoudre le problème n° 1, vous devez déboguer davantage ce qui bloque le thread principal
lorsque l'erreur ANR se produit, vous pouvez le faire
ApplicationExitInfo#getTraceInputStream()
pour obtenir la pierre tombale
trace lorsque l'erreur ANR se produit. Si vous parvenez à reproduire manuellement l'ANR,
vous pouvez enregistrer une trace système et l'inspecter à l'aide de l'une des méthodes
Android Studio ou Perfetto pour mieux comprendre ce qui fonctionne
le thread principal lorsque l'erreur ANR se produit.
Notez que cela peut se produire lorsque vous utilisez directement l'API JobScheduler.
ou en utilisant WorkManager de la bibliothèque Androidx.
Pour résoudre le problème 2, envisagez de migrer vers WorkManager, qui permet d'encapsuler tout traitement dans onStartJob
ou onStopJob
dans un thread asynchrone.
JobScheduler
introduit également une exigence pour déclarer le
Autorisation ACCESS_NETWORK_STATE
si vous utilisez setRequiredNetworkType
ou
setRequiredNetwork
. Si votre application ne déclare pas l'autorisation ACCESS_NETWORK_STATE
lors de la planification de la tâche et qu'elle cible Android 14 ou version ultérieure, une erreur SecurityException
s'affiche.
API de lancement des cartes
Pour les applications ciblant l'âge de 14 ans ou plus,
TileService#startActivityAndCollapse(Intent)
est obsolète et génère désormais
une exception lorsqu'elle est appelée. Si votre application lance des activités à partir de cartes, utilisez plutôt TileService#startActivityAndCollapse(PendingIntent)
.
Confidentialité
Accès limité aux photos et vidéos
Android 14 引入了“已选照片访问权限”,让用户可以向应用授予对其媒体库中特定图片和视频的访问权限,而不是授予对给定类型的所有媒体的访问权限。
只有当您的应用以 Android 14(API 级别 34)或更高版本为目标平台时,此更改才会启用。如果您尚未使用照片选择器,我们建议您在应用中实现照片选择器,以提供一致的图片和视频选择体验,同时增强用户隐私保护,而无需请求任何存储权限。
如果您使用存储权限维护自己的图库选择器,并且需要对实现保持完全控制,请调整实现以使用新的 READ_MEDIA_VISUAL_USER_SELECTED
权限。如果您的应用不使用新权限,系统会以兼容模式运行您的应用。
Expérience utilisateur
Notifications d'intent plein écran sécurisées
Avec Android 11 (niveau d'API 30), toutes les applications pouvaient utiliser Notification.Builder.setFullScreenIntent
pour envoyer des intents plein écran lorsque le téléphone était verrouillé. Vous pouvez l'accorder automatiquement lors de l'installation de l'application en déclarant l'autorisation USE_FULL_SCREEN_INTENT
dans le fichier AndroidManifest.
Les notifications d'intent plein écran sont conçues pour les notifications à priorité très élevée nécessitant l'attention immédiate de l'utilisateur, comme les appels entrants ou les paramètres d'alarme configurés par l'utilisateur. Pour les applications ciblant Android 14 (niveau d'API 34) ou version ultérieure, seules les applications qui proposent des fonctionnalités d'appel et d'alarmes peuvent utiliser cette autorisation. Le Google Play Store révoque les autorisations USE_FULL_SCREEN_INTENT
par défaut pour toutes les applications qui ne correspondent pas à ce profil. La date limite pour appliquer ces modifications est le 31 mai 2024.
Cette autorisation reste activée pour les applications installées sur le téléphone avant que l'utilisateur passe à Android 14. Les utilisateurs peuvent activer et désactiver cette autorisation.
Vous pouvez utiliser la nouvelle API NotificationManager.canUseFullScreenIntent
pour vérifier si votre application dispose de l'autorisation. Sinon, votre application peut utiliser le nouvel intent ACTION_MANAGE_APP_USE_FULL_SCREEN_INTENT
pour afficher la page des paramètres où les utilisateurs peuvent accorder l'autorisation.
Sécurité
Restrictions concernant les intents implicites et en attente
Pour les applications ciblant Android 14 (niveau d'API 34) ou version ultérieure, Android empêche les applications d'envoyer des intents implicites à des composants d'application internes comme suit:
- Les intents implicites ne sont transmis qu'aux composants exportés. Les applications doivent utiliser un intent explicite pour transmettre les composants non exportés, ou marquer le composant comme exporté.
- Si une application crée un intent en attente modifiable avec un intent qui ne spécifie pas de composant ou de package, le système génère une exception.
Ces modifications empêchent les applications malveillantes d'intercepter les intents implicites destinés à être utilisés par les composants internes d'une application.
Par exemple, voici un filtre d'intent qui peut être déclaré dans le fichier manifeste de votre application:
<activity
android:name=".AppActivity"
android:exported="false">
<intent-filter>
<action android:name="com.example.action.APP_ACTION" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
Si votre application a tenté de lancer cette activité à l'aide d'un intent implicite, une exception ActivityNotFoundException
sera générée :
Kotlin
// Throws an ActivityNotFoundException exception when targeting Android 14. context.startActivity(Intent("com.example.action.APP_ACTION"))
Java
// Throws an ActivityNotFoundException exception when targeting Android 14. context.startActivity(new Intent("com.example.action.APP_ACTION"));
Pour lancer l'activité non exportée, votre application doit utiliser un intent explicite :
Kotlin
// This makes the intent explicit. val explicitIntent = Intent("com.example.action.APP_ACTION") explicitIntent.apply { package = context.packageName } context.startActivity(explicitIntent)
Java
// This makes the intent explicit. Intent explicitIntent = new Intent("com.example.action.APP_ACTION") explicitIntent.setPackage(context.getPackageName()); context.startActivity(explicitIntent);
Les broadcasts receivers enregistrés lors de l'exécution doivent spécifier le comportement d'exportation
Les applications et les services qui ciblent Android 14 (niveau d'API 34) ou version ultérieure et utilisent des récepteurs enregistrés en contexte doivent spécifier un indicateur pour indiquer si le récepteur doit être, ou non, exporté vers toutes les autres applications de l'appareil: RECEIVER_EXPORTED
ou RECEIVER_NOT_EXPORTED
, respectivement.
Cette exigence permet de protéger les applications contre les failles de sécurité en tirant parti des fonctionnalités pour ces récepteurs introduites dans Android 13.
Exception pour les récepteurs qui ne reçoivent que des annonces du système
Si votre application n'enregistre un récepteur que pour les annonces du système via les méthodes Context#registerReceiver
, telles que Context#registerReceiver()
, aucun indicateur ne doit être spécifié lors de l'enregistrement du récepteur.
Chargement de code dynamique plus sécurisé
Si votre application cible Android 14 (niveau d'API 34) ou une version ultérieure et utilise le chargement dynamique du code (DCL), tous les fichiers chargés dynamiquement doivent être marqués en lecture seule. Sinon, le système génère une exception. Nous vous recommandons d'éviter le chargement dynamique de code dans la mesure du possible, car cela augmente considérablement le risque que l'application soit compromise par une injection ou une falsification de code.
Si vous devez charger le code de manière dynamique, utilisez l'approche suivante pour définir le fichier chargé dynamiquement (tel qu'un fichier DEX, JAR ou APK) en lecture seule dès que le fichier est ouvert et avant toute écriture :
Kotlin
val jar = File("DYNAMICALLY_LOADED_FILE.jar") val os = FileOutputStream(jar) os.use { // Set the file to read-only first to prevent race conditions jar.setReadOnly() // Then write the actual file content } val cl = PathClassLoader(jar, parentClassLoader)
Java
File jar = new File("DYNAMICALLY_LOADED_FILE.jar"); try (FileOutputStream os = new FileOutputStream(jar)) { // Set the file to read-only first to prevent race conditions jar.setReadOnly(); // Then write the actual file content } catch (IOException e) { ... } PathClassLoader cl = new PathClassLoader(jar, parentClassLoader);
Gérer les fichiers chargés dynamiquement préexistants
Pour éviter de générer des exceptions pour les fichiers existants chargés dynamiquement, nous vous recommandons de supprimer et de recréer les fichiers avant de réessayer de les charger dynamiquement dans votre application. Lorsque vous recréez les fichiers, suivez les instructions précédentes pour les marquer en lecture seule au moment de l'écriture. Vous pouvez également libeller à nouveau les fichiers existants en lecture seule, mais dans ce cas, nous vous recommandons vivement de vérifier d'abord l'intégrité des fichiers (par exemple, en vérifiant la signature du fichier correspond à une valeur de confiance), pour protéger votre application des actions malveillantes.
Restrictions supplémentaires concernant le démarrage d'activités en arrière-plan
Pour les applications ciblant Android 14 (niveau d'API 34) ou version ultérieure, le système restreint davantage le moment où les applications sont autorisées à lancer des activités en arrière-plan:
- Lorsqu'une application envoie un
PendingIntent
à l'aide dePendingIntent#send()
ou de méthodes similaires, elle doit l'activer si elle souhaite accorder ses propres privilèges de lancement d'activité en arrière-plan pour déclencher l'intent en attente. Pour l'activer, l'application doit transmettre un bundleActivityOptions
avecsetPendingIntentBackgroundActivityStartMode(MODE_BACKGROUND_ACTIVITY_START_ALLOWED)
. - Lorsqu'une application visible associe un service à une autre application en arrière-plan à l'aide de la méthode
bindService()
, elle doit désormais l'activer si elle souhaite accorder ses propres privilèges de lancement d'activité d'arrière-plan au service lié. Pour l'activer, l'application doit inclure l'indicateurBIND_ALLOW_ACTIVITY_STARTS
lors de l'appel de la méthodebindService()
.
Ces modifications étendent l'ensemble de restrictions existant afin de protéger les utilisateurs en empêchant les applications malveillantes d'utiliser les API de manière abusive pour démarrer des activités d'interruption en arrière-plan.
Traversée de répertoire ZIP
Pour les applications ciblant Android 14 (niveau d'API 34) ou version ultérieure, Android empêche la faille de traversée de répertoire ZIP de la manière suivante : ZipFile(String)
et ZipInputStream.getNextEntry()
génèrent un ZipException
si les noms des entrées de fichiers ZIP contiennent ".." ou commencent par "/".
Les applications peuvent désactiver cette validation en appelant dalvik.system.ZipPathValidator.clearCallback()
.
Consentement de l'utilisateur requis pour chaque session de capture MediaProjection
Pour les applications ciblant Android 14 (niveau d'API 34) ou version ultérieure, une exception SecurityException
est générée par MediaProjection#createVirtualDisplay
dans l'un des scénarios suivants:
- Votre application met en cache le
Intent
renvoyé parMediaProjectionManager#createScreenCaptureIntent
et le transmet plusieurs fois àMediaProjectionManager#getMediaProjection
. - Votre application appelle
MediaProjection#createVirtualDisplay
plusieurs fois sur la même instanceMediaProjection
.
Votre application doit demander à l'utilisateur de donner son consentement avant chaque session de capture. Une session de capture unique correspond à une seule invocation sur MediaProjection#createVirtualDisplay
, et chaque instance MediaProjection
ne doit être utilisée qu'une seule fois.
Gérer les modifications de configuration
Si votre application doit appeler MediaProjection#createVirtualDisplay
pour gérer les modifications de configuration (telles que l'orientation ou la taille de l'écran), vous pouvez suivre ces étapes pour mettre à jour VirtualDisplay
pour l'instance MediaProjection
existante:
- Appelez
VirtualDisplay#resize
avec la nouvelle largeur et la nouvelle hauteur. - Fournissez un nouvel élément
Surface
avec la nouvelle largeur et la nouvelle hauteur àVirtualDisplay#setSurface
.
Enregistrer un rappel
Votre application doit enregistrer un rappel pour gérer les cas où l'utilisateur ne donne pas son autorisation pour poursuivre une session de capture. Pour ce faire, implémentez Callback#onStop
et demandez à votre application de libérer toutes les ressources associées (telles que VirtualDisplay
et Surface
).
Si votre application n'enregistre pas ce rappel, MediaProjection#createVirtualDisplay
génère une exception IllegalStateException
lorsque votre application l'appelle.
Mise à jour des restrictions non SDK
Android 14 包含更新后的受限非 SDK 接口列表(基于与 Android 开发者之间的协作以及最新的内部测试)。在限制使用非 SDK 接口之前,我们会尽可能确保有可用的公开替代方案。
如果您的应用并非以 Android 14 为目标平台,其中一些变更可能不会立即对您产生影响。然而,虽然您目前仍可以使用一些非 SDK 接口(具体取决于应用的目标 API 级别),但只要您使用任何非 SDK 方法或字段,终归存在导致应用出问题的显著风险。
如果您不确定自己的应用是否使用了非 SDK 接口,则可以测试您的应用来进行确认。如果您的应用依赖于非 SDK 接口,您应该开始计划迁移到 SDK 替代方案。然而,我们知道某些应用具有使用非 SDK 接口的有效用例。如果您无法为应用中的某项功能找到使用非 SDK 接口的替代方案,应请求新的公共 API。
Pour en savoir plus sur les modifications apportées à cette version d'Android, consultez la section Mises à jour des restrictions d'interface non SDK dans Android 14. Pour en savoir plus sur les interfaces non SDK en général, consultez la section Restrictions concernant les interfaces non SDK.