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:
- Modifiche al comportamento che interessano le app quando hanno come target Android 17
- Modifiche al comportamento che interessano tutte le app indipendentemente da
targetSdkVersion.
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 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 erroreOutOfMemoryErrore 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 stack di chiamate in risposta.TRIGGER_TYPE_ANOMALY: rileva anomalie delle prestazioni del sistema, ad esempio chiamate 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 analizzare i dati di profilazione.
Trigger di profilazione per le anomalie delle app
Android 17
introduce un servizio di rilevamento delle anomalie on-device che monitora
comportamenti che richiedono molte risorse e potenziali regressioni della 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 binder eccessive e memoria utilizzata eccessiva. Quando un'app viola
i limiti di memoria definiti dal sistema operativo, il trigger di anomalie consente agli sviluppatori di ricevere
dump dell'heap specifici dell'app per identificare e risolvere i problemi di memoria. Inoltre,
per lo spam eccessivo del binder, il trigger di anomalia fornisce un profilo di campionamento dello stack
sulle transazioni del binder.
Questo callback API si verifica prima di qualsiasi applicazione imposta dal sistema. Ad esempio, può aiutare gli sviluppatori a raccogliere dati di debug prima che l'app venga terminata dal sistema per superamento dei limiti di memoria.
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 引入了新的 JobDebugInfo API,可帮助开发者调试其 JobScheduler 作业,了解作业未运行的原因、运行时长以及其他汇总信息。
扩展后的 JobDebugInfo API 的第一个方法是 getPendingJobReasonStats(),该方法会返回一个映射,其中包含作业处于待执行状态的原因及其各自的累计待执行时长。此方法将 getPendingJobReasonsHistory() 和 getPendingJobReasons() 方法联接在一起,可让您了解预定作业未按预期运行的原因,但通过在单个方法中同时提供时长和作业原因,简化了信息检索。
例如,对于指定的 jobId,该方法可能会返回 PENDING_JOB_REASON_CONSTRAINT_CHARGING 和 60000 毫秒的时长,表示作业因未满足充电约束而处于等待状态 60000 毫秒。
Ridurre i wakelock con il supporto del listener per le sveglie consentite in modalità Inattiva
Android 17
引入了 AlarmManager.setExactAndAllowWhileIdle 的新变体,该变体
接受 OnAlarmListener 而不是 PendingIntent。这种基于回调的新机制非常适合目前依赖于连续唤醒锁来执行定期任务的应用,例如维护套接字连接的消息传递应用。
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
Android 联系人选择工具是一个标准化的可浏览界面,供用户与您的应用分享联系人。该选择工具适用于搭载 Android 17(API 级别 37)或更高版本的设备,可提供一种可保护隐私的替代方案,以取代广泛的 READ_CONTACTS 权限。您的应用无需请求访问用户的整个地址簿,而是指定所需的数据字段(例如电话号码或电子邮件地址),然后用户选择要分享的特定联系人。这样,您的应用便只能读取所选数据,从而确保精细控制,同时提供一致的用户体验,并具有内置搜索、个人资料切换和多选功能,而无需构建或维护界面。
如需了解详情,请参阅联系人选择工具文档。
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
Con Android 17, Aggiornamento in tempo reale lancia le API di colorazione semantica per supportare i colori con significato universale.
Le seguenti classi supportano la colorazione semantica:
NotificationNotification.MetricNotification.ProgressStyle.PointNotification.ProgressStyle.Segment
Colorare
- Verde: associato alla sicurezza. Questo colore deve essere utilizzato nel caso in cui informi le persone che si trovano in una situazione di sicurezza.
- 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 migliore impostazione di protezione.
- 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 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.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)
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();