Fonctionnalités et API

Android 17 offre aux développeurs de nouvelles fonctionnalités et API de qualité. Les sections suivantes récapitulent ces fonctionnalités pour vous aider à vous lancer avec les API associées.

Pour obtenir une liste détaillée des nouvelles API, ainsi que des API modifiées et supprimées, consultez le rapport de différences des API. Pour en savoir plus sur les nouvelles API, consultez la documentation de référence des API Android. Les nouvelles API sont mises en évidence pour une meilleure visibilité.

Vous devez également examiner les domaines dans lesquels les changements de plate-forme peuvent affecter vos applications. Pour en savoir plus, consultez les pages suivantes :

Fonctionnalité de base

Android 17 ajoute les nouvelles fonctionnalités suivantes liées à la fonctionnalité Android de base.

Nouveaux déclencheurs ProfilingManager

Android 17 ajoute plusieurs déclencheurs système à ProfilingManager pour vous aider à collecter des données détaillées afin de déboguer les problèmes de performances.

Voici les nouveaux déclencheurs :

  • TRIGGER_TYPE_COLD_START : le déclencheur se produit lors du démarrage à froid de l'application. Il fournit à la fois un exemple de pile d'appels et une trace système dans la réponse.
  • TRIGGER_TYPE_OOM : le déclencheur se produit lorsqu'une application génère une OutOfMemoryError et fournit un vidage du tas Java en réponse.
  • TRIGGER_TYPE_KILL_EXCESSIVE_CPU_USAGE : le déclencheur se produit lorsqu'une application est arrêtée en raison d'une utilisation anormale et excessive du processeur, et fournit un exemple de pile d'appels en réponse.
  • TRIGGER_TYPE_ANOMALY : détectez les anomalies de performances du système, telles que les appels de binder excessifs et l'utilisation excessive de la mémoire.

Pour savoir comment configurer le déclencheur système, consultez la documentation sur le profilage basé sur les déclencheurs et sur la façon de récupérer et d'analyser les données de profilage.

Déclencheur de profilage pour les anomalies d'application

Android 17 introduit un service de détection des anomalies sur l'appareil qui surveille les comportements gourmands en ressources et les régressions de compatibilité potentielles. Intégré à ProfilingManager, ce service permet à votre application de recevoir des artefacts de profilage déclenchés par des événements spécifiques détectés par le système.

Utilisez le déclencheur TRIGGER_TYPE_ANOMALY pour détecter les problèmes de performances du système, tels que les appels Binder excessifs et l'utilisation excessive de mémoire. Lorsqu'une application dépasse les limites de mémoire définies par l'OS, le déclencheur d'anomalie permet aux développeurs de recevoir des vidages du tas spécifiques à l'application pour les aider à identifier et à résoudre les problèmes de mémoire. De plus, pour les spams de liaison excessifs, le déclencheur d'anomalie fournit un profil d'échantillonnage de pile sur les transactions de liaison.

Ce rappel d'API se produit avant toute application imposée par le système. Par exemple, il peut aider les développeurs à collecter des données de débogage avant que l'application ne soit arrêtée par le système pour dépassement des limites de mémoire.

val profilingManager =
    applicationContext.getSystemService(ProfilingManager::class.java)
val triggers = ArrayList<ProfilingTrigger>()
triggers.add(ProfilingTrigger.Builder(ProfilingTrigger.TRIGGER_TYPE_ANOMALY))
val mainExecutor: Executor = Executors.newSingleThreadExecutor()
val resultCallback = Consumer<ProfilingResult> { profilingResult ->
    if (profilingResult.errorCode != ProfilingResult.ERROR_NONE) {
        // upload profile result to server for further analysis
        setupProfileUploadWorker(profilingResult.resultFilePath)
    }
    profilingManager.registerForAllProfilingResults(mainExecutor,
                                                    resultCallback)
    profilingManager.addProfilingTriggers(triggers)
}

API JobDebugInfo

Android 17 introduit de nouvelles API JobDebugInfo pour aider les développeurs à déboguer leurs jobs JobScheduler : pourquoi ils ne s'exécutent pas, combien de temps ils ont duré et d'autres informations agrégées.

La première méthode des API JobDebugInfo développées est getPendingJobReasonStats(), qui renvoie une carte des raisons pour lesquelles le job était dans un état d'exécution en attente et leurs durées cumulées respectives en attente. Cette méthode combine les méthodes getPendingJobReasonsHistory() et getPendingJobReasons() pour vous aider à comprendre pourquoi un job planifié ne s'exécute pas comme prévu. Elle simplifie toutefois la récupération d'informations en rendant la durée et le motif du job disponibles dans une seule méthode.

Par exemple, pour un jobId spécifié, la méthode peut renvoyer PENDING_JOB_REASON_CONSTRAINT_CHARGING et une durée de 60 000 ms, ce qui indique que la tâche était en attente pendant 60 000 ms, car la contrainte de recharge n'était pas respectée.

Réduire les verrous de réveil avec la prise en charge des écouteurs pour les alarmes "autoriser pendant l'inactivité"

Android 17 introduit une nouvelle variante de AlarmManager.setExactAndAllowWhileIdle qui accepte un OnAlarmListener au lieu d'un PendingIntent. Ce nouveau mécanisme basé sur des rappels est idéal pour les applications qui s'appuient actuellement sur des wakelocks continus pour effectuer des tâches périodiques, telles que les applications de messagerie qui maintiennent des connexions de socket.

Confidentialité

Android 17 inclut les nouvelles fonctionnalités suivantes pour améliorer la confidentialité des utilisateurs.

Compatibilité de la plate-forme avec Encrypted Client Hello (ECH)

Android 17 introduit la compatibilité de la plate-forme avec ECH (Encrypted Client Hello), une amélioration significative de la confidentialité pour les communications réseau. ECH est une extension TLS 1.3 qui chiffre l'indication du nom du serveur (SNI) lors du handshake TLS initial. Ce chiffrement permet de protéger la confidentialité des utilisateurs en rendant plus difficile l'identification du domaine spécifique auquel une application se connecte par les intermédiaires réseau.

La plate-forme inclut désormais les API nécessaires pour que les bibliothèques réseau implémentent ECH. Cela inclut de nouvelles fonctionnalités dans DnsResolver pour interroger les enregistrements DNS HTTPS contenant des configurations ECH, ainsi que de nouvelles méthodes dans les SSLEngines et SSLSockets de Conscrypt pour activer ECH en transmettant ces configurations lors de la connexion à un domaine. Les développeurs peuvent configurer les préférences ECH, par exemple en l'activant de manière opportuniste ou en imposant son utilisation, via le nouvel élément <domainEncryption> dans le fichier de configuration de la sécurité réseau, applicable globalement ou par domaine.

Les bibliothèques réseau populaires telles que HttpEngine, WebView et OkHttp devraient intégrer ces API de plate-forme dans de futures mises à jour, ce qui permettra aux applications d'adopter plus facilement ECH et d'améliorer la confidentialité des utilisateurs.

Pour en savoir plus, consultez la documentation Encrypted Client Hello.

Sélecteur de contacts Android

Le sélecteur de contacts Android est une interface standardisée et navigable qui permet aux utilisateurs de partager des contacts avec votre application. Disponible sur les appareils équipés d'Android 17 (niveau d'API 37) ou version ultérieure, il offre une alternative respectueuse de la confidentialité à l'autorisation READ_CONTACTS étendue. Au lieu de demander l'accès à l'intégralité du carnet d'adresses de l'utilisateur, votre application spécifie les champs de données dont elle a besoin, tels que les numéros de téléphone ou les adresses e-mail, et l'utilisateur sélectionne les contacts spécifiques à partager. Cela accorde à votre application un accès en lecture uniquement aux données sélectionnées, ce qui garantit un contrôle précis tout en offrant une expérience utilisateur cohérente avec des fonctionnalités de recherche, de changement de profil et de sélection multiple intégrées, sans avoir à créer ni à gérer l'UI.

Pour en savoir plus, consultez la documentation sur le sélecteur de contacts.

Sécurité

Android 17 ajoute les nouvelles fonctionnalités suivantes pour améliorer la sécurité des appareils et des applications.

Mode Protection Avancée Android (AAPM)

Le mode Protection Avancée d'Android offre aux utilisateurs Android un nouvel ensemble de fonctionnalités de sécurité puissantes. Il s'agit d'une étape importante pour protéger les utilisateurs, en particulier ceux qui sont plus exposés, contre les attaques sophistiquées. Conçue comme une fonctionnalité optionnelle, l'AAPM s'active avec un seul paramètre de configuration que les utilisateurs peuvent activer à tout moment pour appliquer un ensemble de protections de sécurité.

Ces configurations de base incluent le blocage de l'installation d'applications provenant de sources inconnues (téléchargement indépendant), la restriction de la signalisation des données USB et l'analyse obligatoire de Google Play Protect, ce qui réduit considérablement la surface d'attaque de l'appareil. Les développeurs peuvent s'intégrer à cette fonctionnalité à l'aide de l'API AdvancedProtectionManager pour détecter l'état du mode, ce qui permet aux applications d'adopter automatiquement une posture de sécurité renforcée ou de restreindre les fonctionnalités à haut risque lorsqu'un utilisateur a activé le mode.

Signature d'APK avec la PQC

Android est désormais compatible avec un schéma de signature des APK hybride pour protéger l'identité de signature de votre application contre la menace potentielle d'attaques utilisant l'informatique quantique. Cette fonctionnalité introduit un nouveau schéma de signature APK, qui vous permet d'associer une clé de signature classique (telle que RSA ou EC) à un nouvel algorithme de cryptographie post-quantique (PQC, Post-Quantum Cryptography) (ML-DSA).

Cette approche hybride garantit que votre application reste sécurisée contre les futures attaques quantiques tout en maintenant une rétrocompatibilité totale avec les anciennes versions d'Android et les appareils qui s'appuient sur la validation de signature classique.

Impact sur les développeurs

  • Applications utilisant la signature d'application Play : si vous utilisez la signature d'application Play, vous pouvez attendre que Google Play vous propose de mettre à niveau une signature hybride à l'aide d'une clé PQC générée par Google Play. Votre application sera ainsi protégée sans que vous ayez à gérer manuellement les clés.
  • Applications utilisant des clés autogérées : les développeurs qui gèrent leurs propres clés de signature peuvent utiliser des outils de compilation Android mis à jour (comme apksigner) pour passer à une identité hybride, combinant une clé PQC à une nouvelle clé classique. (Vous devez créer une clé classique, vous ne pouvez pas réutiliser l'ancienne.)

Connectivité

Android 17 ajoute les fonctionnalités suivantes pour améliorer la connectivité des appareils et des applications.

Réseaux satellites contraints

Implements optimizations to enable apps to function effectively over low-bandwidth satellite networks.

Expérience utilisateur et UI du système

Android 17 inclut les modifications suivantes pour améliorer l'expérience utilisateur.

Flux de volume dédié à l'Assistant

Android 17 introduit un flux de volume dédié à l'Assistant pour les applications de l'Assistant, pour la lecture avec USAGE_ASSISTANT. Cette modification dissocie l'audio de l'Assistant du flux multimédia standard, ce qui permet aux utilisateurs de contrôler les deux volumes de manière isolée. Cela permet de couper le son de la lecture multimédia tout en conservant l'audibilité des réponses de l'Assistant, et inversement.

Les applications d'assistance ayant accès au nouveau mode audio MODE_ASSISTANT_CONVERSATION peuvent améliorer davantage la cohérence du contrôle du volume. Les applications de l'Assistant peuvent utiliser ce mode pour fournir un indice au système concernant une session Assistant active, ce qui permet de contrôler le flux de l'Assistant en dehors de la lecture USAGE_ASSISTANT active ou avec des périphériques Bluetooth connectés.

Handoff

Le transfert est une nouvelle fonctionnalité et une nouvelle API qui seront disponibles dans Android 17. Les développeurs d'applications pourront les intégrer pour offrir une continuité inter-appareils à leurs utilisateurs. Il permet à l'utilisateur de démarrer une activité d'application sur un appareil Android et de la transférer vers un autre appareil Android. Le transfert s'exécute en arrière-plan de l'appareil d'un utilisateur et affiche les activités disponibles à partir des autres appareils à proximité de l'utilisateur via différents points d'entrée, comme le lanceur d'applications et la barre des tâches, sur l'appareil de réception.

Les applications peuvent désigner le transfert pour lancer la même application Android native, si elle est installée et disponible sur l'appareil de réception. Dans ce flux d'application à application, l'utilisateur est redirigé vers l'activité désignée à l'aide d'un lien profond. Vous pouvez également proposer le transfert d'application à Web comme option de secours ou l'implémenter directement avec le transfert d'URL.

La prise en charge du transfert est implémentée par activité. Pour activer le transfert, appelez la méthode setHandoffEnabled() pour l'activité. Des données supplémentaires peuvent être nécessaires lors du transfert pour que l'activité recréée sur l'appareil de réception puisse restaurer l'état approprié. Implémentez le rappel onHandoffActivityDataRequested() pour renvoyer un objet HandoffActivityData contenant des informations qui spécifient comment Handoff doit gérer et recréer l'activité sur l'appareil de réception.

Mise à jour en direct : API de couleurs sémantiques

Avec Android 17, Live Update lance les API de coloration sémantique pour prendre en charge les couleurs ayant une signification universelle.

Les classes suivantes sont compatibles avec la coloration sémantique :

Jeux de coloriage

  • Vert : associé à la sécurité. Cette couleur doit être utilisée pour indiquer aux utilisateurs que vous êtes en sécurité.
  • Orange : pour désigner la prudence et signaler les risques physiques. Cette couleur doit être utilisée lorsque les utilisateurs doivent faire attention à définir un meilleur paramètre de protection.
  • Rouge : indique généralement un danger ou un arrêt. Il doit être présenté dans le cas où l'attention des utilisateurs est requise de toute urgence.
  • Bleu : couleur neutre pour le contenu informatif qui doit se démarquer des autres contenus.

L'exemple suivant montre comment appliquer des styles sémantiques au texte d'une notification :

  val ssb = SpannableStringBuilder()
        .append("Colors: ")
        .append("NONE", Notification.createSemanticStyleAnnotation(SEMANTIC_STYLE_UNSPECIFIED), 0)
        .append(", ")
        .append("INFO", Notification.createSemanticStyleAnnotation(SEMANTIC_STYLE_INFO), 0)
        .append(", ")
        .append("SAFE", Notification.createSemanticStyleAnnotation(SEMANTIC_STYLE_SAFE), 0)
        .append(", ")
        .append("CAUTION", Notification.createSemanticStyleAnnotation(SEMANTIC_STYLE_CAUTION), 0)
        .append(", ")
        .append("DANGER", Notification.createSemanticStyleAnnotation(SEMANTIC_STYLE_DANGER), 0)

    Notification.Builder(context, channelId)
          .setSmallIcon(R.drawable.ic_icon)
          .setContentTitle("Hello World!")
          .setContentText(ssb)
          .setOngoing(true)
              .setRequestPromotedOngoing(true)

API UWB Downlink-TDoA pour Android 17

Downlink Time Difference of Arrival (DL-TDoA) ranging lets a device determine its position relative to multiple anchors by measuring the relative arrival times of signals.

The following snippet demonstrates how to initialize the Ranging Manager, verify device capabilities, and start a DL-TDoA session:

Kotlin

class RangingApp {

    fun initDlTdoa(context: Context) {
        // Initialize the Ranging Manager
        val rangingManager = context.getSystemService(RangingManager::class.java)

        // Register for device capabilities
        val capabilitiesCallback = object : RangingManager.RangingCapabilitiesCallback {
            override fun onRangingCapabilities(capabilities: RangingCapabilities) {
                // Make sure Dl-TDoA is supported before starting the session
                if (capabilities.uwbCapabilities != null && capabilities.uwbCapabilities!!.isDlTdoaSupported) {
                    startDlTDoASession(context)
                }
            }
        }
        rangingManager.registerCapabilitiesCallback(Executors.newSingleThreadExecutor(), capabilitiesCallback)
    }

    fun startDlTDoASession(context: Context) {

        // Initialize the Ranging Manager
        val rangingManager = context.getSystemService(RangingManager::class.java)

        // Create session and configure parameters
        val executor = Executors.newSingleThreadExecutor()
        val rangingSession = rangingManager.createRangingSession(executor, RangingSessionCallback())
        val rangingRoundIndexes = byteArrayOf(0)
        val config: ByteArray = byteArrayOf() // OOB config data
        val params = DlTdoaRangingParams.createFromFiraConfigPacket(config, rangingRoundIndexes)

        val rangingDevice = RangingDevice.Builder().build()
        val rawTagDevice = RawRangingDevice.Builder()
            .setRangingDevice(rangingDevice)
            .setDlTdoaRangingParams(params)
            .build()

        val dtTagConfig = RawDtTagRangingConfig.Builder(rawTagDevice).build()

        val preference = RangingPreference.Builder(DEVICE_ROLE_DT_TAG, dtTagConfig)
            .setSessionConfig(SessionConfig.Builder().build())
            .build()

        // Start the ranging session
        rangingSession.start(preference)
    }
}

private class RangingSessionCallback : RangingSession.Callback {
    override fun onDlTdoaResults(peer: RangingDevice, measurement: DlTdoaMeasurement) {
        // Process measurement results here
    }
}

Java

public class RangingApp {

    public void initDlTdoa(Context context) {

        // Initialize the Ranging Manager
        RangingManager rangingManager = context.getSystemService(RangingManager.class);

        // Register for device capabilities
        RangingManager.CapabilitiesCallback capabilitiesCallback = new RangingManager.RangingCapabilitiesCallback() {
            @Override
            public void onRangingCapabilities(RangingCapabilities capabilities) {
                // Make sure Dl-TDoA is supported before starting the session
                if (capabilities.getUwbCapabilities() != null && capabilities.getUwbCapabilities().isDlTdoaSupported()) {
                    startDlTDoASession(context);
                }
            }
        };
        rangingManager.registerCapabilitiesCallback(Executors.newSingleThreadExecutor(), capabilitiesCallback);
    }

    public void startDlTDoASession(Context context) {
        RangingManager rangingManager = context.getSystemService(RangingManager.class);

        // Create session and configure parameters
        Executor executor = Executors.newSingleThreadExecutor();
        RangingSession rangingSession = rangingManager.createRangingSession(executor, new RangingSessionCallback());
        byte[] rangingRoundIndexes = new byte[] {0};
        byte[] config = new byte[0]; // OOB config data
        DlTdoaRangingParams params = DlTdoaRangingParams.createFromFiraConfigPacket(config, rangingRoundIndexes);

        RangingDevice rangingDevice = new RangingDevice.Builder().build();
        RawRangingDevice rawTagDevice = new RawRangingDevice.Builder()
                .setRangingDevice(rangingDevice)
                .setDlTdoaRangingParams(params)
                .build();

        RawDtTagRangingConfig dtTagConfig = new RawDtTagRangingConfig.Builder(rawTagDevice).build();

        RangingPreference preference = new RangingPreference.Builder(DEVICE_ROLE_DT_TAG, dtTagConfig)
                .setSessionConfig(new SessionConfig.Builder().build())
                .build();

        // Start the ranging session
        rangingSession.start(preference);
    }

    private static class RangingSessionCallback implements RangingSession.Callback {

        @Override
        public void onDlTdoaResults(RangingDevice peer, DlTdoaMeasurement measurement) {
            // Process measurement results here
        }
    }
}

Out-of-Band (OOB) Configurations

The following snippet provides an example of DL-TDoA OOB configuration data for Wi-Fi and BLE:

Java

// Wifi Configuration
byte[] wifiConfig = {
    (byte) 0xDD, (byte) 0x2D, (byte) 0x5A, (byte) 0x18, (byte) 0xFF, // Header
    (byte) 0x5F, (byte) 0x19, // FiRa Sub-Element
    (byte) 0x02, (byte) 0x00, // Profile ID
    (byte) 0x06, (byte) 0x02, (byte) 0x20, (byte) 0x08, // MAC Address
    (byte) 0x14, (byte) 0x01, (byte) 0x0C, // Preamble Index
    (byte) 0x27, (byte) 0x02, (byte) 0x08, (byte) 0x07, // Vendor ID
    (byte) 0x28, (byte) 0x06, (byte) 0xCA, (byte) 0xC8, (byte) 0xA6, (byte) 0xF7, (byte) 0x6F, (byte) 0x08, // Static STS IV
    (byte) 0x08, (byte) 0x02, (byte) 0x60, (byte) 0x09, // Slot Duration
    (byte) 0x1B, (byte) 0x01, (byte) 0x0A, // Slots per RR
    (byte) 0x09, (byte) 0x04, (byte) 0xE8, (byte) 0x03, (byte) 0x00, (byte) 0x00, // Duration
    (byte) 0x9F, (byte) 0x04, (byte) 0x67, (byte) 0x45, (byte) 0x23, (byte) 0x01  // Session ID
};

// BLE Configuration
byte[] bleConfig = {
    (byte) 0x2D, (byte) 0x16, (byte) 0xF4, (byte) 0xFF, // Header
    (byte) 0x5F, (byte) 0x19, // FiRa Sub-Element
    (byte) 0x02, (byte) 0x00, // Profile ID
    (byte) 0x06, (byte) 0x02, (byte) 0x20, (byte) 0x08, // MAC Address
    (byte) 0x14, (byte) 0x01, (byte) 0x0C, // Preamble Index
    (byte) 0x27, (byte) 0x02, (byte) 0x08, (byte) 0x07, // Vendor ID
    (byte) 0x28, (byte) 0x06, (byte) 0xCA, (byte) 0xC8, (byte) 0xA6, (byte) 0xF7, (byte) 0x6F, (byte) 0x08, // Static STS IV
    (byte) 0x08, (byte) 0x02, (byte) 0x60, (byte) 0x09, // Slot Duration
    (byte) 0x1B, (byte) 0x01, (byte) 0x0A, // Slots per RR
    (byte) 0x09, (byte) 0x04, (byte) 0xE8, (byte) 0x03, (byte) 0x00, (byte) 0x00, // Duration
    (byte) 0x9F, (byte) 0x04, (byte) 0x67, (byte) 0x45, (byte) 0x23, (byte) 0x01  // Session ID
};

If you can't use an OOB configuration because it is missing, or if you need to change default values that aren't in the OOB config, you can build parameters with DlTdoaRangingParams.Builder as shown in the following snippet. You can use these parameters in place of DlTdoaRangingParams.createFromFiraConfigPacket():

Kotlin

val dlTdoaParams = DlTdoaRangingParams.Builder(1)
    .setComplexChannel(UwbComplexChannel.Builder()
            .setChannel(9).setPreambleIndex(10).build())
    .setDeviceAddress(deviceAddress)
    .setSessionKeyInfo(byteArrayOf(0x01, 0x02, 0x03, 0x04))
    .setRangingIntervalMillis(240)
    .setSlotDuration(UwbRangingParams.DURATION_2_MS)
    .setSlotsPerRangingRound(20)
    .setRangingRoundIndexes(byteArrayOf(0x01, 0x05))
    .build()

Java

DlTdoaRangingParams dlTdoaParams = new DlTdoaRangingParams.Builder(1)
    .setComplexChannel(new UwbComplexChannel.Builder()
            .setChannel(9).setPreambleIndex(10).build())
    .setDeviceAddress(deviceAddress)
    .setSessionKeyInfo(new byte[]{0x01, 0x02, 0x03, 0x04})
    .setRangingIntervalMillis(240)
    .setSlotDuration(UwbRangingParams.DURATION_2_MS)
    .setSlotsPerRangingRound(20)
    .setRangingRoundIndexes(new byte[]{0x01, 0x05})
    .build();