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 il Riferimento 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 alle funzionalità principali di Android.

Nuovi trigger ProfilingManager

Android 17 向 ProfilingManager 添加了多个新的系统触发器,以 帮助您收集深入数据来调试性能问题。

新触发器包括:

如需了解如何设置系统触发器,请参阅有关 基于触发器的性能分析的文档以及有关如何检索和分析性能分析数据 的文档

应用异常的性能分析触发器

Android 17 引入了一项设备端异常检测服务,用于监控资源密集型行为和潜在的兼容性回归。此服务与ProfilingManager集成,可让您的应用接收由特定系统检测到的事件触发的性能分析工件。

使用 TRIGGER_TYPE_ANOMALY 触发器检测系统性能问题 例如 binder 调用过多和内存用量过高。当应用违反操作系统定义的内存限制时,异常触发器允许开发者接收特定于应用的堆转储,以帮助识别和修复内存问题。此外,对于 binder 垃圾内容过多,异常触发器会提供有关 binder 事务的堆栈抽样分析报告。

此 API 回调发生在系统强制执行任何操作之前。例如,它可以帮助开发者在应用因超出内存限制而被系统终止之前收集调试数据。

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

Ridurre i wakelock con il supporto del listener per le sveglie consentite in modalità Inattiva

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 basano su 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 sfogliabile 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 rispetta la privacy all'ampia 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 contatti specifici da condividere. In questo modo, la tua app avrà accesso in lettura solo ai dati selezionati, garantendo un controllo granulare e fornendo un'esperienza utente coerente con funzionalità di ricerca, cambio profilo e selezione multipla integrate senza dover creare o gestire la UI.

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à.

Firma dell'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 quantum computing. Questa funzionalità introduce un nuovo schema di firma dell'APK, che 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 e i dispositivi Android precedenti 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 la possibilità di eseguire l'upgrade di una firma ibrida utilizzando una chiave PQC generata da Google Play, garantendo la protezione della tua app senza richiedere la gestione manuale delle chiavi.
  • App che utilizzano chiavi autogestite:gli sviluppatori che gestiscono le proprie chiavi di firma possono utilizzare 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.

Stream del volume dell'assistente dedicato

Android 17 introduce un flusso di volume dell'assistente dedicato per le app dell'assistente, per la riproduzione con USAGE_ASSISTANT. Questa modifica disaccoppia l'audio dell'assistente dal flusso multimediale standard, fornendo agli utenti un controllo isolato su entrambi i volumi. In questo modo è possibile, ad esempio, disattivare l'audio della riproduzione multimediale mantenendo l'udibilità delle risposte dell'assistente e viceversa.

Le app dell'assistente con accesso alla nuova MODE_ASSISTANT_CONVERSATION modalità audio possono migliorare ulteriormente la coerenza del controllo del volume. Le app dell'assistente possono utilizzare questa modalità per fornire al sistema un suggerimento su una sessione dell'assistente attiva, assicurandosi che il flusso dell'assistente possa essere controllato al di fuori della riproduzione USAGE_ASSISTANT attiva o con periferiche Bluetooth collegate.

Handoff

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

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

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

Aggiornamento in tempo reale - API dei colori semantici

在 Android 17 中,实时更新启动了语义着色 API,以支持具有通用含义的颜色。

以下类支持语义着色:

填色游戏

  • 绿色:与安全相关。此颜色应在以下情况下使用:让别人知道您处于安全状态。
  • 橙色:用于表示警告和标记物理危险。在用户需要注意以设置更好的保护设置的情况下,应使用此颜色。
  • 红色:通常表示危险、停止。它应在需要人们紧急关注的情况下显示。
  • 蓝色:中性颜色,适用于信息性内容,应与其他内容区分开来。

以下示例展示了如何将语义样式应用于通知中的文本:

  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

下行链路到达时间差 (DL-TDoA) 测距功能可让设备通过测量信号的相对到达时间来确定其相对于多个锚点的相对位置。

以下代码段演示了如何初始化 测距管理器、 验证设备功能以及启动 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
        }
    }
}

带外 (OOB) 配置

以下代码段提供了 Wi-Fi 和 BLE 的 DL-TDoA OOB 配置数据示例:

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

如果您无法使用 OOB 配置(因为该配置缺失),或者需要更改 OOB 配置中没有的默认值,则可以使用 DlTdoaRangingParams.Builder 构建参数,如以下代码段所示。您可以使用这些参数代替 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();