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 adds several new system triggers to ProfilingManager to
help you collect in-depth data to debug performance issues.
The new triggers are:
TRIGGER_TYPE_COLD_START: Trigger occurs during app cold start. It provides both a call stack sample and a system trace in the response.TRIGGER_TYPE_OOM: Trigger occurs when an app throws anOutOfMemoryErrorand provides a Java Heap Dump in response.TRIGGER_TYPE_KILL_EXCESSIVE_CPU_USAGE: Trigger occurs when an app is killed due to abnormal and excessive CPU usage and provides a call stack sample in response.TRIGGER_TYPE_ANOMALY: Detect system performance anomalies such as excessive binder calls and excessive memory usage.
To understand how to set up the system trigger, see the documentation on trigger-based profiling and how to retrieve and analyze profiling data documentation.
Profiling trigger for app anomalies
Android 17
introduces an on-device anomaly detection service that monitors for
resource-intensive behaviors and potential compatibility regressions. Integrated
with ProfilingManager, this service allows your app to receive profiling
artifacts triggered by specific system-detected events.
Use the TRIGGER_TYPE_ANOMALY trigger to detect system performance issues
such as excessive binder calls and excessive memory usage. When an app breaches
OS-defined memory limits, the anomaly trigger allows developers to receive
app-specific heap dumps to help identify and fix memory issues. Additionally,
for excessive binder spam, the anomaly trigger provides a stack sampling profile
on binder transactions.
This API callback occurs prior to any system imposed enforcements. For example, it can help developers collect debug data before the app is terminated by the system for exceeding memory limits.
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 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.
Selettore di contatti 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.
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 now supports a hybrid APK signature scheme to future-proof your app's signing identity against the potential threat of attacks that make use of quantum computing. This feature introduces a new APK Signature Scheme, which lets you pair a classical signing key (such as RSA or EC) with a new post-quantum cryptography (PQC) algorithm (ML-DSA).
This hybrid approach ensures your app remains secure against future quantum attacks while maintaining full backward compatibility with older Android versions and devices that rely on classical signature verification.
Impact on developers
- Apps using Play App Signing: If you use Play App Signing, you can wait for Google Play to give you the option to upgrade a hybrid signature using a PQC key generated by Google Play, ensuring your app is protected without requiring manual key management.
- Apps using self-managed keys: Developers who manage their own signing keys can utilize updated Android build tools (like apksigner) to rotate to a hybrid identity, combining a PQC key with a new classical key. (You must create a new classical key, you cannot reuse the older one.)
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 differenza di tempo di arrivo del downlink (DL-TDoA) consente a un dispositivo di determinare la propria posizione rispetto a più ancoraggi misurando i tempi di arrivo relativi dei segnali.
Lo snippet che segue 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 che segue 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é manca o se devi modificare i valori predefiniti che non sono presenti nella configurazione OOB, puoi creare parametri con DlTdoaRangingParams.Builder, come mostrato nello snippet che segue. 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();