Funzionalità e API

Android 17 introduce nuove fantastiche funzionalità e API per gli sviluppatori. Le sezioni seguenti riassumono queste funzionalità per aiutarti a iniziare a utilizzare le API correlate.

Per un elenco dettagliato delle API nuove, modificate e rimosse, leggi il report sulle differenze delle API. Per informazioni dettagliate sulle nuove API, consulta il Riferimento API Android. Le nuove API sono evidenziate per una maggiore visibilità.

Dovresti anche esaminare le aree in cui le modifiche alla piattaforma potrebbero influire sulle tue app. Per saperne di più, consulta le pagine seguenti:

Funzionalità di base

Android 17 aggiunge le seguenti nuove funzionalità relative alle funzionalità principali di Android.

Nuovi trigger ProfilingManager

Android 17 aggiunge diversi nuovi trigger di sistema a ProfilingManager per aiutarti a raccogliere dati approfonditi per eseguire il debug dei problemi di prestazioni.

I nuovi attivatori sono:

  • TRIGGER_TYPE_COLD_START: l'attivatore si verifica durante l'avvio a freddo dell'app. Fornisce sia un esempio di stack di chiamate sia un tracciamento del sistema nella risposta.
  • TRIGGER_TYPE_OOM: l'attivazione avviene quando un'app genera un'eccezione OutOfMemoryError e fornisce un dump dell'heap Java in risposta.
  • TRIGGER_TYPE_KILL_EXCESSIVE_CPU_USAGE: l'attivatore si attiva quando un'app viene terminata a causa di un utilizzo anomalo ed eccessivo della CPU e fornisce un'istantanea di un tracciamento del sistema in background in esecuzione in risposta.
  • TRIGGER_TYPE_ANOMALY: rileva anomalie delle prestazioni del sistema, come chiamate di binder eccessive e memoria utilizzata eccessiva.

Per capire come configurare il trigger di sistema, consulta la documentazione sulla profilazione basata su trigger e su come recuperare e caricare i profili e analizzare i dati di profilazione.

Trigger di profilazione per le anomalie delle app

Android 17 introduce un servizio di rilevamento di anomalie on-device che monitora i comportamenti che richiedono un uso intensivo delle risorse e le potenziali regressioni di compatibilità. Integrato con ProfilingManager, questo servizio consente alla tua app di ricevere artefatti di profilazione attivati da eventi specifici rilevati dal sistema.

Utilizza il trigger TRIGGER_TYPE_ANOMALY per rilevare problemi di prestazioni del sistema, come chiamate di binder eccessive e memoria utilizzata eccessiva. Quando un'app supera i limiti di memoria definiti dal sistema operativo, l'attivatore di anomalie consente agli sviluppatori di ricevere dump dell'heap specifici per l'app per identificare e risolvere i problemi di memoria. Inoltre, per lo spam eccessivo di binder, l'attivatore di anomalie fornisce un profilo di campionamento dello stack sulle transazioni di binder.

Questo callback dell'API si verifica prima di qualsiasi applicazione imposta dal sistema. Ad esempio, può aiutare gli sviluppatori a raccogliere i dati di debug prima che l'app venga terminata dal sistema per aver superato i limiti di memoria.

fun registerAnomalyTrigger(context: Context) {
    val profilingManager =
        context.getSystemService(ProfilingManager::class.java) ?: return
    val triggers = listOf(
        ProfilingTrigger.Builder(ProfilingTrigger.TRIGGER_TYPE_ANOMALY).build()
    )
    val callbackExecutor: Executor = Executors.newSingleThreadExecutor()
    val resultCallback = Consumer<ProfilingResult> { profilingResult ->
        if (profilingResult.errorCode == ProfilingResult.ERROR_NONE) {
            // Upload profile result to server for further analysis
            profilingResult.resultFilePath?.let { setupProfileUploadWorker(it) }
        }
    }
    profilingManager.registerForAllProfilingResults(
        callbackExecutor,
        resultCallback
    )
    profilingManager.addProfilingTriggers(triggers)
}

API JobDebugInfo

Android 17 introduce nuove API JobDebugInfo per aiutare gli sviluppatori a eseguire il debug dei job JobScheduler: perché non vengono eseguiti, per quanto tempo sono stati eseguiti e altri dati aggregati.

Il primo metodo delle API JobDebugInfo ampliate è getPendingJobReasonStats(), che restituisce una mappa dei motivi per cui il job era in uno stato di esecuzione in attesa e delle rispettive durate cumulative in attesa. Questo metodo unisce i metodi getPendingJobReasonsHistory() e getPendingJobReasons() per darti informazioni sul motivo per cui un job pianificato non viene eseguito come previsto, ma semplifica il recupero delle informazioni rendendo disponibili sia la durata sia il motivo del job in un unico metodo.

Ad esempio, per un jobId specificato, il metodo potrebbe restituire PENDING_JOB_REASON_CONSTRAINT_CHARGING e una durata di 60.000 ms, a indicare che il job era in attesa per 60.000 ms perché il vincolo di ricarica non era soddisfatto.

Riduci i wakelock con il supporto dei listener per le sveglie attive in modalità di sospensione

Android 17 introduce una nuova variante di AlarmManager.setExactAndAllowWhileIdle che accetta un OnAlarmListener anziché un PendingIntent. Questo nuovo meccanismo basato su callback è ideale per le app che attualmente si affidano a wakelock continui per eseguire attività periodiche, come le app di messaggistica che mantengono le connessioni socket.

Privacy

Android 17 include le seguenti nuove funzionalità per migliorare la privacy degli utenti.

Supporto della piattaforma Encrypted Client Hello (ECH)

Android 17 introduce il supporto della piattaforma per Encrypted Client Hello (ECH), un miglioramento significativo della privacy per le comunicazioni di rete. ECH è un'estensione TLS 1.3 che cripta l'indicazione del nome del server (SNI) durante l'handshake TLS iniziale. Questa crittografia contribuisce a proteggere la privacy degli utenti rendendo più difficile per gli intermediari di rete identificare il dominio specifico a cui si connette un'app.

La piattaforma ora include le API necessarie per le librerie di networking per implementare ECH. Sono incluse nuove funzionalità in DnsResolver per eseguire query per record DNS HTTPS contenenti configurazioni ECH e nuovi metodi in SSLEngines e SSLSockets di Conscrypt per attivare ECH passando queste configurazioni quando ci si connette a un dominio. Gli sviluppatori possono configurare le preferenze ECH, ad esempio abilitandole in modo opportunistico o rendendone obbligatorio l'utilizzo, tramite il nuovo elemento <domainEncryption> all'interno del file di configurazione della sicurezza di rete, applicabile a livello globale o per singolo dominio.

È previsto che le librerie di rete più diffuse, come HttpEngine, WebView e OkHttp, integrino queste API della piattaforma negli aggiornamenti futuri, semplificando l'adozione di ECH da parte delle app e migliorando la privacy degli utenti.

Per saperne di più, consulta la documentazione relativa a Encrypted Client Hello.

Selettore di contatti Android

Il Selettore di contatti Android è un'interfaccia standardizzata e consultabile che consente agli utenti di condividere i contatti con la tua app. Disponibile sui dispositivi con Android 17 (livello API 37) o versioni successive, il selettore offre un'alternativa che tutela la privacy all'autorizzazione READ_CONTACTS generale. Anziché richiedere l'accesso all'intera rubrica dell'utente, l'app specifica i campi di dati di cui ha bisogno, ad esempio numeri di telefono o indirizzi email, e l'utente seleziona i contatti specifici da condividere. In questo modo, la tua app ottiene l'accesso in lettura solo ai dati selezionati, garantendo un controllo granulare e offrendo al contempo un'esperienza utente coerente con funzionalità integrate di ricerca, cambio di profilo della Ricerca e selezione multipla senza dover creare o gestire l'interfaccia utente.

Per saperne di più, consulta la documentazione sul selettore di contatti.

Sicurezza

Android 17 aggiunge le seguenti nuove funzionalità per migliorare la sicurezza dei dispositivi e delle app.

Modalità di protezione avanzata di Android (AAPM)

La modalità di protezione avanzata di Android offre agli utenti Android un nuovo e potente insieme di funzionalità di sicurezza, segnando un passo significativo nella salvaguardia degli utenti, in particolare di quelli a rischio più elevato, da attacchi sofisticati. Progettato come funzionalità di attivazione, AAPM viene attivato con una singola impostazione di configurazione che gli utenti possono attivare in qualsiasi momento per applicare un insieme di protezioni di sicurezza.

Queste configurazioni di base includono il blocco dell'installazione di app da origini sconosciute (sideloading), la limitazione della segnalazione dei dati USB e l'obbligo di scansione di Google Play Protect, che riduce notevolmente la superficie di attacco del dispositivo. Gli sviluppatori possono integrarsi con questa funzionalità utilizzando l'API AdvancedProtectionManager per rilevare lo stato della modalità, consentendo alle applicazioni di adottare automaticamente una postura di sicurezza rafforzata o limitare le funzionalità ad alto rischio quando un utente ha attivato la funzionalità.

Firma APK PQC

Android ora supporta uno schema di firma dell'APK ibrido per proteggere l'identità di firma della tua app dalla potenziale minaccia di attacchi che utilizzano il computing quantistico. Questa funzionalità introduce un nuovo schema di firma APK, che ti consente di accoppiare una chiave di firma classica (come RSA o EC) con un nuovo algoritmo di crittografia post-quantistica (PQC) (ML-DSA).

Questo approccio ibrido garantisce che la tua app rimanga protetta da futuri attacchi quantistici, mantenendo al contempo la piena compatibilità con le versioni precedenti di Android e i dispositivi che si basano sulla verifica della firma classica.

Impatto sugli sviluppatori

  • App che utilizzano la firma dell'app di Google Play:se utilizzi la firma dell'app di Google Play, puoi attendere che Google Play ti offra l'opzione di eseguire l'upgrade di una firma ibrida utilizzando una chiave PQC generata da Google Play, assicurandoti che la tua app sia protetta senza richiedere la gestione manuale delle chiavi.
  • App che utilizzano chiavi autogestite:gli sviluppatori che gestiscono le proprie chiavi di firma possono utilizzare gli strumenti di build Android aggiornati (come apksigner) per passare a un'identità ibrida, combinando una chiave PQC con una nuova chiave classica. Devi creare una nuova chiave classica, non puoi riutilizzare quella precedente.

Connettività

Android 17 aggiunge le seguenti funzionalità per migliorare la connettività di dispositivi e app.

Reti satellitari con limitazioni

Implementa ottimizzazioni per consentire alle app di funzionare in modo efficace su reti satellitari a bassa larghezza di banda.

Esperienza utente e UI di sistema

Android 17 include le seguenti modifiche per migliorare l'esperienza utente.

Volume stream dell'assistente dedicato

Android 17 introduce uno stream del volume dell'assistente dedicato per le app di assistenza, per la riproduzione con USAGE_ASSISTANT. Questa modifica separa l'audio dell'assistente dallo stream multimediale standard, offrendo agli utenti un controllo isolato su entrambi i volumi. In questo modo è possibile, ad esempio, disattivare l'audio della riproduzione di contenuti multimediali mantenendo l'udibilità delle risposte dell'assistente e viceversa.

Le app di assistenza con accesso alla nuova modalità audio MODE_ASSISTANT_CONVERSATION possono migliorare ulteriormente la coerenza del controllo del volume. Le app di assistenza possono utilizzare questa modalità per fornire un suggerimento al sistema in merito a una sessione dell'assistente attiva, assicurando che lo stream dell'assistente possa essere controllato al di fuori della riproduzione USAGE_ASSISTANT attiva o con periferiche Bluetooth connesse.

Handoff

Handoff è una nuova funzionalità e API in arrivo su Android 17 che gli sviluppatori di app possono integrare per fornire continuità tra dispositivi ai propri utenti. Consente all'utente di avviare un'attività dell'app su un dispositivo Android e di trasferirla su un altro dispositivo Android. Handoff viene eseguito in background sul dispositivo di un utente e mostra le attività disponibili dagli altri dispositivi dell'utente nelle vicinanze tramite vari punti di accesso, come Avvio app e la barra delle app, sul dispositivo di destinazione.

Le app possono designare Handoff per avviare la stessa app per Android nativa, se è installata e disponibile sul dispositivo di destinazione. In questo flusso da app ad app, l'utente viene indirizzato tramite deep link all'attività designata. In alternativa, l'handoff da app a web può essere offerto come opzione di riserva o implementato direttamente con l'handoff dell'URL.

Il supporto di Handoff è implementato per ogni attività. Per attivare Handoff, chiama il metodo setHandoffEnabled() per l'attività. Potrebbe essere necessario trasmettere dati aggiuntivi insieme all'handoff, in modo che l'attività ricreata sul dispositivo di destinazione possa ripristinare lo stato appropriato. Implementa il callback onHandoffActivityDataRequested() per restituire un oggetto HandoffActivityData che contiene i dettagli che specificano in che modo Handoff deve gestire e ricreare l'attività sul dispositivo di destinazione.

Aggiornamento in tempo reale - API dei colori semantici

Con Android 17, Live Update lancia le API di colorazione semantica per supportare i colori con un significato universale.

Le seguenti classi supportano la colorazione semantica:

Colorare

  • Verde: associato alla sicurezza. Questo colore deve essere utilizzato per far sapere alle persone che ti trovi in una situazione sicura.
  • Arancione: per indicare cautela e segnalare pericoli fisici. Questo colore deve essere utilizzato nella situazione in cui gli utenti devono prestare attenzione per impostare una protezione migliore.
  • Rosso: in genere indica pericolo, stop. Deve essere presentato nel caso in cui sia necessario attirare urgentemente l'attenzione delle persone.
  • Blu: colore neutro per i contenuti informativi che devono distinguersi dagli altri contenuti.

L'esempio seguente mostra come applicare stili semantici al testo di una notifica:

  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 per Android 17

Il rilevamento della distanza Downlink Time Difference of Arrival (DL-TDoA) consente a un dispositivo di determinare la sua posizione rispetto a più anchor misurando i tempi di arrivo relativi dei segnali.

Il seguente snippet mostra come inizializzare il Ranging Manager, verificare le funzionalità del dispositivo e avviare una sessione 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
        }
    }
}

Configurazioni fuori banda (OOB)

Il seguente snippet fornisce un esempio di dati di configurazione OOB DL-TDoA per Wi-Fi e 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
};

Se non puoi utilizzare una configurazione OOB perché non è presente o se devi modificare i valori predefiniti che non sono presenti nella configurazione OOB, puoi creare parametri con DlTdoaRangingParams.Builder come mostrato nello snippet seguente. Puoi utilizzare questi parametri al posto di 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();