Funzionalità e API

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

Per un elenco dettagliato delle API nuove, modificate e rimosse, leggi il report diff API. Per informazioni dettagliate sulle nuove API, visita la documentazione di riferimento delle API Android. Le nuove API sono evidenziate per una maggiore visibilità.

Devi anche esaminare le aree in cui le modifiche alla piattaforma potrebbero influire sulle tue app. Per maggiori informazioni, consulta le seguenti pagine:

Funzionalità di base

Android 17 aggiunge le seguenti nuove funzionalità relative alla funzionalità di base 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 rendimento.

I nuovi attivatori sono:

  • TRIGGER_TYPE_COLD_START: il trigger si verifica durante l'avvio a freddo dell'app. Fornisce sia un campione dello stack di chiamate sia una traccia di sistema nella risposta.
  • TRIGGER_TYPE_OOM: il trigger si attiva quando un'app genera un errore OutOfMemoryError e fornisce un dump dell'heap Java in risposta.
  • TRIGGER_TYPE_KILL_EXCESSIVE_CPU_USAGE: il trigger si attiva quando un'app viene chiusa a causa di un utilizzo anomalo ed eccessivo della CPU e fornisce un esempio di call stack in risposta.

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

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 altre informazioni aggregate.

Il primo metodo delle API JobDebugInfo estese è getPendingJobReasonStats(), che restituisce una mappa dei motivi per cui il job era in uno stato di esecuzione in attesa e le rispettive durate cumulative in attesa. Questo metodo si unisce ai metodi getPendingJobReasonsHistory() e getPendingJobReasons() per fornirti informazioni sul motivo per cui un job pianificato non viene eseguito come previsto, ma semplifica il recupero delle informazioni rendendo disponibili sia la durata che 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, indicando che il job è rimasto in attesa per 60.000 ms perché il vincolo di ricarica non è stato soddisfatto.

Privacy

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

Selettore contatti Android

Il selettore di contatti Android è un'interfaccia standardizzata e sfogliabile che consente agli utenti di condividere i contatti con la tua app. Disponibile sui dispositivi con Android 17 o versioni successive, il selettore offre un'alternativa che tutela la privacy all'autorizzazione READ_CONTACTS. Anziché richiedere l'accesso all'intera rubrica dell'utente, la tua 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 fornendo al contempo un'esperienza utente coerente con funzionalità di ricerca, cambio di profilo e selezione multipla integrate, senza dover creare o gestire l'interfaccia utente.

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

Sicurezza

Android 17 aggiunge le seguenti nuove funzionalità per migliorare la sicurezza di dispositivi e 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à.

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.

Handoff

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

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

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

Aggiornamento in tempo reale - API dei colori semantici

Con Android 17, Aggiornamento in tempo reale 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 nel caso in cui informi le persone che ti trovi in una situazione di sicurezza.
  • Arancione: per indicare cautela e segnalare pericoli fisici. Questo colore deve essere utilizzato in situazione in cui gli utenti devono prestare attenzione per impostare una migliore protezione impostazione.
  • Rosso: in genere indica pericolo, stop. Deve essere presentato nel caso in cui sia necessaria urgentemente l'attenzione delle persone.
  • Blu: colore neutro per i contenuti che sono informativi e devono distinguersi dagli altri contenuti.

Il seguente esempio mostra come applicare stili semantici al testo in 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

La misurazione della distanza tramite la differenza di tempo di arrivo del downlink (DL-TDoA) consente a un dispositivo di determinare la sua posizione rispetto a più punti di ancoraggio misurando i tempi di arrivo relativi dei segnali.

Il seguente snippet mostra come inizializzare 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.CapabilitiesCallback {
            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 = intArrayOf(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.CapabilitiesCallback() {
            @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());
        int[] rangingRoundIndexes = new int[] {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)

Lo snippet seguente 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 nel seguente snippet. 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();