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, 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 nouveaux déclencheurs système à ProfilingManager pour vous aider à collecter des données détaillées afin de déboguer les problèmes de performances.

Les nouveaux déclencheurs sont les suivants :

  • 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 de 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étecte les anomalies de performances du système, telles que les appels de liaison 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 la documentation sur la récupération et l’analyse des 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 de liaison excessifs et l'utilisation excessive de la mémoire. Lorsqu'une application dépasse les limites de mémoire définies par le système d'exploitation, le déclencheur d'anomalie permet aux développeurs de recevoir des vidages de tas spécifiques à l'application pour les aider à identifier et à résoudre les problèmes de mémoire. De plus, en cas de spam de liaison excessif, 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 introduces new JobDebugInfo APIs to help developers debug their JobScheduler jobs--why they aren't running, how long they ran for, and other aggregated information.

The first method of the expanded JobDebugInfo APIs is getPendingJobReasonStats(), which returns a map of reasons why the job was in a pending execution state and their respective cumulative pending durations. This method joins the getPendingJobReasonsHistory() and getPendingJobReasons() methods to give you insight into why a scheduled job is not running as expected, but simplifies information retrieval by making both duration and job reason available in a single method.

For example, for a specified jobId, the method might return PENDING_JOB_REASON_CONSTRAINT_CHARGING and a duration of 60000 ms, indicating the job was pending for 60000ms due to the charging constraint not being satisfied.

Réduire les verrous de réveil avec la prise en charge des écouteurs pour les alarmes "allow-while-idle"

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 introduces platform support for Encrypted Client Hello (ECH), a significant privacy enhancement for network communications. ECH is a TLS 1.3 extension that encrypts the Server Name Indication (SNI) during the initial TLS handshake. This encryption helps protect user privacy by making it more difficult for network intermediaries to identify the specific domain an app is connecting to.

The platform now includes the necessary APIs for networking libraries to implement ECH. This includes new capabilities in DnsResolver to query for HTTPS DNS records containing ECH configurations, and new methods in Conscrypt's SSLEngines and SSLSockets to enable ECH by passing in these configurations when connecting to a domain. Developers can configure ECH preferences, such as enabling it opportunistically or mandating its use, through the new <domainEncryption> element within the Network Security Configuration file, applicable globally or on a per-domain basis.

Popular networking libraries such as HttpEngine, WebView, and OkHttp are expected to integrate these platform APIs in future updates, making it easier for apps to adopt ECH and enhance user privacy.

For more information, see the Encrypted Client Hello documentation.

Sélecteur de contacts Android

The Android Contact Picker is a standardized, browsable interface for users to share contacts with your app. Available on devices running Android 17 (API level 37) or higher, the picker offers a privacy-preserving alternative to the broad READ_CONTACTS permission. Instead of requesting access to the user's entire address book, your app specifies the data fields it needs, such as phone numbers or email addresses, and the user selects specific contacts to share. This grants your app read access to only the selected data, ensuring granular control while providing a consistent user experience with built-in search, profile switching, and multi-selection capabilities without having to build or maintain the UI.

For more information, see the contact picker documentation.

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 PQC

Android est désormais compatible avec un schéma de signature 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) (ML-DSA).

Cette approche hybride garantit que votre application reste sécurisée contre les futures attaques quantiques tout en conservant une compatibilité ascendante totale avec les anciennes versions d'Android et les appareils qui s'appuient sur la vérification classique des signatures.

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, en combinant une clé PQC avec 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

Implémente des optimisations pour permettre aux applications de fonctionner efficacement sur les réseaux satellites à faible bande passante.

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 introduces a dedicated Assistant volume stream for Assistant apps, for playback with USAGE_ASSISTANT. This change decouples Assistant audio from the standard media stream, providing users with isolated control over both volumes. This enables scenarios such as muting media playback while maintaining audibility for Assistant responses, and the other way around.

Assistant apps with access to the new MODE_ASSISTANT_CONVERSATION audio mode can further improve the volume control consistency. Assistant apps can use this mode to provide a hint to the system about an active Assistant session, ensuring the Assistant stream can be controlled outside of the active USAGE_ASSISTANT playback or with connected Bluetooth peripherals.

Transfert

Handoff is a new feature and API coming to Android 17 that app developers can integrate with to provide cross-device continuity for their users. It allows the user to start an app activity on one Android device and transition it to another Android device. Handoff runs in the background of a user's device and surfaces available activities from the user's other nearby devices through various entry points, like the launcher and taskbar, on the receiving device.

Apps can designate Handoff to launch the same native Android app, if it is installed and available on the receiving device. In this app-to-app flow, the user is deep-linked to the designated activity. Alternatively, app-to-web Handoff can be offered as a fallback option or directly implemented with URL Handoff.

Handoff support is implemented on a per-activity basis. To enable Handoff, call the setHandoffEnabled() method for the activity. Additional data may need to be passed along with the handoff so the recreated activity on the receiving device can restore appropriate state. Implement the onHandoffActivityDataRequested() callback to return a HandoffActivityData object which contains details that specify how Handoff should handle and recreate the activity on the receiving device.

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

With Android 17, Live Update launches the Semantic Coloring APIs to support colors with universal meaning.

The following classes support semantic coloring:

Coloring

  • Green: Associated with safety. This color should be used for the case where it lets people know you are in the safe situation.
  • Orange: For designating caution and marking physical hazards. This color should be used in the situation where users need to pay attention to set better protection setting.
  • Red: Generally indicates danger, stop. It should be presented for the case where need people's attention urgently.
  • Blue: Neutral color for content that is informational and should stand out from other content.

The following example shows how to apply semantic styles to text in a 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

La télémétrie par différence de temps d'arrivée en liaison descendante (DL-TDoA) permet à un appareil de déterminer sa position par rapport à plusieurs points d'ancrage en mesurant les temps d'arrivée relatifs des signaux.

L'extrait de code suivant montre comment initialiser le gestionnaire de télémétrie, vérifier les fonctionnalités de l'appareil et démarrer une session DL-TDoA :

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
        }
    }
}

Configurations hors bande

L'extrait de code suivant fournit un exemple de données de configuration hors bande DL-TDoA pour le Wi-Fi et le 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
};

Si vous ne pouvez pas utiliser de configuration hors bande, car elle est manquante, ou si vous devez modifier des valeurs par défaut qui ne figurent pas dans la configuration hors bande, vous pouvez créer des paramètres avec DlTdoaRangingParams.Builder, comme illustré dans l'extrait de code suivant. Vous pouvez utiliser ces paramètres à la place de 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();