Le funzionalità Wi-Fi Aware consentono ai dispositivi con Android 8.0 (livello API 26) e versioni successive di rilevarsi e connettersi direttamente tra loro senza altri tipi di connettività. Wi-Fi Aware è noto anche come Neighbor Awareness Networking (NAN).
Il networking Wi-Fi Aware funziona formando cluster con i dispositivi vicini o creando un nuovo cluster se il dispositivo è il primo in una zona. Questo comportamento di clustering si applica all'intero dispositivo ed è gestito dal servizio di sistema Wi-Fi Aware; le app non hanno alcun controllo sul comportamento di clustering. Le app utilizzano le API Wi-Fi Aware per comunicare con il servizio di sistema Wi-Fi Aware, che gestisce l'hardware Wi-Fi Aware sul dispositivo.
Le API Wi-Fi Aware consentono alle app di eseguire le seguenti operazioni:
Scopri altri dispositivi: l'API ha un meccanismo per trovare altri dispositivi nelle vicinanze. Il processo inizia quando un dispositivo pubblica uno o più servizi rilevabili. Poi, quando un dispositivo si abbona a uno o più servizi ed entra nel raggio d'azione del Wi-Fi dell'editore, l'abbonato riceve una notifica che indica che è stato scoperto un editore corrispondente. Dopo che l'abbonato ha scoperto un editore, può inviare un breve messaggio o stabilire una connessione di rete con il dispositivo scoperto. I dispositivi possono essere contemporaneamente publisher e abbonati.
Crea una connessione di rete: dopo che due dispositivi si sono scoperti a vicenda, possono creare una connessione di rete Wi-Fi Aware bidirezionale senza un punto di accesso.
Le connessioni di rete Wi-Fi Aware supportano velocità effettive più elevate su distanze più lunghe rispetto alle connessioni Bluetooth. Questi tipi di connessioni sono utili per le app che condividono grandi quantità di dati tra gli utenti, come le app di condivisione di foto.
Miglioramenti di Android 13 (livello API 33)
Sui dispositivi con Android 13 (livello API 33) e versioni successive che supportano la modalità di comunicazione istantanea, le app possono utilizzare i metodi PublishConfig.Builder.setInstantCommunicationModeEnabled()
e SubscribeConfig.Builder.setInstantCommunicationModeEnabled()
per attivare o disattivare la modalità di comunicazione istantanea per una sessione di rilevamento di publisher o abbonati. La modalità di comunicazione istantanea velocizza lo scambio di messaggi,
l'individuazione dei servizi e la configurazione di qualsiasi percorso dei dati nell'ambito di una sessione di individuazione
di publisher o abbonati. Per determinare se un dispositivo supporta la modalità di comunicazione istantanea, utilizza il metodo isInstantCommunicationModeSupported()
.
Miglioramenti di Android 12 (livello API 31)
Android 12 (livello API 31) aggiunge alcuni miglioramenti a Wi-Fi Aware:
- Sui dispositivi con Android 12 (livello API 31) o versioni successive, puoi utilizzare il
callback
onServiceLost()
per ricevere una notifica quando la tua app ha perso un servizio rilevato perché il servizio si è interrotto o è fuori dal raggio d'azione. - La configurazione dei percorsi dati Wi-Fi Aware è stata semplificata. Le versioni precedenti utilizzavano la messaggistica L2 per fornire l'indirizzo MAC dell'iniziatore, il che introduceva latenza. Sui dispositivi con Android 12 e versioni successive, il risponditore (server) può essere configurato per accettare qualsiasi peer, ovvero non deve conoscere in anticipo l'indirizzo MAC dell'iniziatore. Ciò accelera l'attivazione del percorso dati e consente più collegamenti point-to-point con una sola richiesta di rete.
- Le app in esecuzione su Android 12 o versioni successive possono utilizzare il metodo
WifiAwareManager.getAvailableAwareResources()
per ottenere il numero di percorsi di dati attualmente disponibili, pubblicare sessioni e iscriversi alle sessioni. In questo modo l'app può determinare se ci sono risorse disponibili sufficienti per eseguire la funzionalità desiderata.
Configurazione iniziale
Per configurare l'app in modo che utilizzi la scoperta e la rete Wi-Fi Aware, segui questi passaggi:
Richiedi le seguenti autorizzazioni nel manifest della tua app:
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" /> <uses-permission android:name="android.permission.INTERNET" /> <!-- If your app targets Android 13 (API level 33) or higher, you must declare the NEARBY_WIFI_DEVICES permission. --> <uses-permission android:name="android.permission.NEARBY_WIFI_DEVICES" <!-- If your app derives location information from Wi-Fi APIs, don't include the "usesPermissionFlags" attribute. --> android:usesPermissionFlags="neverForLocation" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" <!-- If any feature in your app relies on precise location information, don't include the "maxSdkVersion" attribute. --> android:maxSdkVersion="32" />
Verifica se il dispositivo supporta Wi-Fi Aware con l'API
PackageManager
come mostrato di seguito:Kotlin
context.packageManager.hasSystemFeature(PackageManager.FEATURE_WIFI_AWARE)
Java
context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_AWARE);
Controlla se Wi-Fi Aware è attualmente disponibile. Wi-Fi Aware potrebbe essere presente sul dispositivo, ma potrebbe non essere attualmente disponibile perché l'utente ha disattivato il Wi-Fi o la posizione. A seconda delle funzionalità hardware e firmware, alcuni dispositivi potrebbero non supportare Wi-Fi Aware se sono in uso Wi-Fi Direct, SoftAP o tethering. Per verificare se Wi-Fi Aware è attualmente disponibile, chiama
isAvailable()
.La disponibilità di Wi-Fi Aware può variare in qualsiasi momento. La tua app deve registrare un
BroadcastReceiver
per ricevereACTION_WIFI_AWARE_STATE_CHANGED
, che viene inviato ogni volta che la disponibilità cambia. Quando la tua app riceve l'intent di trasmissione, deve eliminare tutte le sessioni esistenti (supponendo che il servizio Wi-Fi Aware sia stato interrotto), quindi controllare lo stato attuale della disponibilità e modificare il proprio comportamento di conseguenza. Ad esempio:Kotlin
val wifiAwareManager = context.getSystemService(Context.WIFI_AWARE_SERVICE) as WifiAwareManager? val filter = IntentFilter(WifiAwareManager.ACTION_WIFI_AWARE_STATE_CHANGED) val myReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { // discard current sessions if (wifiAwareManager?.isAvailable) { ... } else { ... } } } context.registerReceiver(myReceiver, filter)
Java
WifiAwareManager wifiAwareManager = (WifiAwareManager)context.getSystemService(Context.WIFI_AWARE_SERVICE) IntentFilter filter = new IntentFilter(WifiAwareManager.ACTION_WIFI_AWARE_STATE_CHANGED); BroadcastReceiver myReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { // discard current sessions if (wifiAwareManager.isAvailable()) { ... } else { ... } } }; context.registerReceiver(myReceiver, filter);
Per ulteriori informazioni, consulta Trasmissioni.
Ottenere una sessione
Per iniziare a utilizzare Wi-Fi Aware, la tua app deve ottenere un
WifiAwareSession
chiamando
attach()
. Questo metodo
esegue le seguenti operazioni:
- Attiva l'hardware Wi-Fi Aware.
- Si unisce a un cluster Wi-Fi Aware o ne forma uno.
- Crea una sessione Wi-Fi Aware con uno spazio dei nomi univoco che funge da contenitore per tutte le sessioni di rilevamento create al suo interno.
Se l'app viene collegata correttamente, il sistema esegue il
callback onAttached()
.
Questo callback fornisce un oggetto WifiAwareSession
che la tua app deve utilizzare per tutte le operazioni di sessione successive. Un'app può utilizzare la sessione per pubblicare un servizio o abbonarsi a un servizio.
La tua app deve chiamare
attach()
una sola volta. Se
la tua app chiama attach()
più volte, riceve una sessione diversa per ogni chiamata, ognuna con il proprio spazio dei nomi. Questa opzione può essere utile in scenari complessi, ma in genere deve essere evitata.
Pubblicare un servizio
Per rendere rilevabile un servizio, chiama il metodo
publish()
, che
accetta i seguenti parametri:
PublishConfig
specifica il nome del servizio e altre proprietà di configurazione, ad esempio il filtro di corrispondenza.DiscoverySessionCallback
specifica le azioni da eseguire quando si verificano eventi, ad esempio quando l'abbonato riceve un messaggio.
Ecco un esempio:
Kotlin
val config: PublishConfig = PublishConfig.Builder() .setServiceName(AWARE_FILE_SHARE_SERVICE_NAME) .build() awareSession.publish(config, object : DiscoverySessionCallback() { override fun onPublishStarted(session: PublishDiscoverySession) { ... } override fun onMessageReceived(peerHandle: PeerHandle, message: ByteArray) { ... } })
Java
PublishConfig config = new PublishConfig.Builder() .setServiceName(“Aware_File_Share_Service_Name”) .build(); awareSession.publish(config, new DiscoverySessionCallback() { @Override public void onPublishStarted(PublishDiscoverySession session) { ... } @Override public void onMessageReceived(PeerHandle peerHandle, byte[] message) { ... } }, null);
Se la pubblicazione va a buon fine, viene chiamato il metodo di callback
onPublishStarted()
.
Dopo la pubblicazione, quando i dispositivi che eseguono app per abbonati corrispondenti si spostano nel
raggio di copertura Wi-Fi del dispositivo di pubblicazione, gli abbonati scoprono il servizio. Quando
un abbonato scopre un editore, quest'ultimo non riceve una
notifica; se l'abbonato invia un messaggio all'editore, quest'ultimo
riceve una notifica. In questo caso, viene chiamato il metodo di callback onMessageReceived()
. Puoi utilizzare l'argomento
PeerHandle
di questo metodo per
inviare un messaggio all'abbonato o
creare una connessione.
Per interrompere la pubblicazione del servizio, chiama
DiscoverySession.close()
.
Le sessioni di esplorazione sono associate al relativo elemento padre
WifiAwareSession
. Se la sessione principale viene
chiusa, vengono chiuse anche le sessioni di esplorazione associate. Anche gli oggetti eliminati
vengono chiusi, ma il sistema non garantisce la chiusura delle sessioni
non incluse nell'ambito, pertanto ti consigliamo di chiamare esplicitamente i metodi close()
.
Abbonarsi a un servizio
Per abbonarti a un servizio, chiama il metodo
subscribe()
, che accetta i seguenti parametri:
-
SubscribeConfig
specifica il nome del servizio a cui abbonarsi e altre proprietà di configurazione, come il filtro di corrispondenza. DiscoverySessionCallback
specifica le azioni da eseguire quando si verificano eventi, ad esempio quando viene rilevato un publisher.
Ecco un esempio:
Kotlin
val config: SubscribeConfig = SubscribeConfig.Builder() .setServiceName(AWARE_FILE_SHARE_SERVICE_NAME) .build() awareSession.subscribe(config, object : DiscoverySessionCallback() { override fun onSubscribeStarted(session: SubscribeDiscoverySession) { ... } override fun onServiceDiscovered( peerHandle: PeerHandle, serviceSpecificInfo: ByteArray, matchFilter: List<ByteArray> ) { ... } }, null)
Java
SubscribeConfig config = new SubscribeConfig.Builder() .setServiceName("Aware_File_Share_Service_Name") .build(); awareSession.subscribe(config, new DiscoverySessionCallback() { @Override public void onSubscribeStarted(SubscribeDiscoverySession session) { ... } @Override public void onServiceDiscovered(PeerHandle peerHandle, byte[] serviceSpecificInfo, List<byte[]> matchFilter) { ... } }, null);
Se l'operazione di iscrizione va a buon fine, il sistema chiama il callback
onSubscribeStarted()
nella tua app. Poiché puoi utilizzare l'argomento
SubscribeDiscoverySession
nel
callback per comunicare con un editore dopo che la tua app ne ha scoperto uno, devi
salvare questo riferimento. Puoi aggiornare la sessione di iscrizione in qualsiasi momento chiamando
updateSubscribe()
nella sessione di scoperta.
A questo punto, l'abbonamento attende che i publisher corrispondenti rientrino nel
raggio del Wi-Fi. Quando ciò accade, il sistema esegue il metodo di callback
onServiceDiscovered()
. Puoi utilizzare l'argomento PeerHandle
di questo callback per inviare un messaggio o creare una connessione a questo editore.
Per annullare l'abbonamento a un servizio, chiama
DiscoverySession.close()
.
Le sessioni di esplorazione sono associate al relativo elemento padre
WifiAwareSession
. Se la sessione principale viene
chiusa, vengono chiuse anche le sessioni di esplorazione associate. Anche gli oggetti eliminati
vengono chiusi, ma il sistema non garantisce la chiusura delle sessioni
non incluse nell'ambito, pertanto ti consigliamo di chiamare esplicitamente i metodi close()
.
Inviare un messaggio
Per inviare un messaggio a un altro dispositivo, devi disporre dei seguenti oggetti:
A
DiscoverySession
. Questo oggetto ti consente di chiamaresendMessage()
. La tua app ottiene unDiscoverySession
pubblicando un servizio o abbonandosi a un servizio.L'
PeerHandle
dell'altro dispositivo, per indirizzare il messaggio. La tua app riceve l'PeerHandle
di un altro dispositivo in due modi:- La tua app pubblica un servizio e riceve un messaggio da un abbonato.
La tua app riceve
PeerHandle
dell'abbonato dal callbackonMessageReceived()
. - La tua app si abbona a un servizio. Quando rileva un editore corrispondente, la tua app riceve il
PeerHandle
dell'editore dal callbackonServiceDiscovered()
.
- La tua app pubblica un servizio e riceve un messaggio da un abbonato.
La tua app riceve
Per inviare un messaggio, chiama
sendMessage()
. Potrebbero quindi verificarsi i seguenti callback:
- Quando il messaggio viene ricevuto correttamente dal peer, il sistema chiama il
callback
onMessageSendSucceeded()
nell'app mittente. - Quando il peer riceve un messaggio, il sistema chiama il callback
onMessageReceived()
nell'app ricevente.
Sebbene l'PeerHandle
sia necessario per comunicare con i peer, non devi
farci affidamento come identificatore permanente dei peer. Gli identificatori di livello superiore possono essere
utilizzati dall'applicazione, incorporati nel servizio di rilevamento stesso o in
messaggi successivi. Puoi incorporare un identificatore nel servizio di rilevamento con il metodo setMatchFilter()
o setServiceSpecificInfo()
di PublishConfig
o SubscribeConfig
. Il metodo
setMatchFilter()
influisce sulla scoperta, mentre il metodo
setServiceSpecificInfo()
non influisce sulla scoperta.
L'incorporamento di un identificatore in un messaggio implica la modifica dell'array di byte del messaggio in modo da includere un identificatore (ad esempio, come i primi due byte).
Crea una connessione
Wi-Fi Aware supporta il networking client-server tra due dispositivi Wi-Fi Aware.
Per configurare la connessione client-server:
Utilizza il rilevamento Wi-Fi Aware per pubblicare un servizio (sul server) e abbonarti a un servizio (sul client).
Una volta che il sottoscrittore ha scoperto il publisher, invia un messaggio dal sottoscrittore al publisher.
Avvia un
ServerSocket
sul dispositivo del publisher e imposta o ottieni la porta:Kotlin
val ss = ServerSocket(0) val port = ss.localPort
Java
ServerSocket ss = new ServerSocket(0); int port = ss.getLocalPort();
Utilizza
ConnectivityManager
per richiedere una rete Wi-Fi Aware sul publisher utilizzando unWifiAwareNetworkSpecifier
, specificando la sessione di rilevamento e l'PeerHandle
dell'abbonato, che hai ottenuto dal messaggio trasmesso dall'abbonato:Kotlin
val networkSpecifier = WifiAwareNetworkSpecifier.Builder(discoverySession, peerHandle) .setPskPassphrase("somePassword") .setPort(port) .build() val myNetworkRequest = NetworkRequest.Builder() .addTransportType(NetworkCapabilities.TRANSPORT_WIFI_AWARE) .setNetworkSpecifier(networkSpecifier) .build() val callback = object : ConnectivityManager.NetworkCallback() { override fun onAvailable(network: Network) { ... } override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) { ... } override fun onLost(network: Network) { ... } } connMgr.requestNetwork(myNetworkRequest, callback);
Java
NetworkSpecifier networkSpecifier = new WifiAwareNetworkSpecifier.Builder(discoverySession, peerHandle) .setPskPassphrase("somePassword") .setPort(port) .build(); NetworkRequest myNetworkRequest = new NetworkRequest.Builder() .addTransportType(NetworkCapabilities.TRANSPORT_WIFI_AWARE) .setNetworkSpecifier(networkSpecifier) .build(); ConnectivityManager.NetworkCallback callback = new ConnectivityManager.NetworkCallback() { @Override public void onAvailable(Network network) { ... } @Override public void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities) { ... } @Override public void onLost(Network network) { ... } }; ConnectivityManager connMgr.requestNetwork(myNetworkRequest, callback);
Una volta che il publisher richiede una rete, deve inviare un messaggio all'abbonato.
Una volta che l'abbonato riceve il messaggio dall'editore, richiedi una rete Wi-Fi Aware sull'abbonato utilizzando lo stesso metodo dell'editore. Non specificare una porta quando crei
NetworkSpecifier
. I metodi di callback appropriati vengono chiamati quando la connessione di rete è disponibile, modificata o persa.Una volta chiamato il metodo
onAvailable()
sul sottoscrittore, è disponibile un oggettoNetwork
con cui puoi aprire unSocket
per comunicare conServerSocket
sull'editore, ma devi conoscere l'indirizzo IPv6 e la porta diServerSocket
. Questi valori vengono recuperati dall'oggettoNetworkCapabilities
fornito nel callbackonCapabilitiesChanged()
:Kotlin
val peerAwareInfo = networkCapabilities.transportInfo as WifiAwareNetworkInfo val peerIpv6 = peerAwareInfo.peerIpv6Addr val peerPort = peerAwareInfo.port ... val socket = network.getSocketFactory().createSocket(peerIpv6, peerPort)
Java
WifiAwareNetworkInfo peerAwareInfo = (WifiAwareNetworkInfo) networkCapabilities.getTransportInfo(); Inet6Address peerIpv6 = peerAwareInfo.getPeerIpv6Addr(); int peerPort = peerAwareInfo.getPort(); ... Socket socket = network.getSocketFactory().createSocket(peerIpv6, peerPort);
Al termine della connessione di rete, chiama
unregisterNetworkCallback()
.
Peer di ranging e rilevamento basato sulla posizione
Un dispositivo con funzionalità di localizzazione Wi-Fi RTT può misurare direttamente la distanza dai peer e utilizzare queste informazioni per limitare l'individuazione dei servizi Wi-Fi Aware.
L'API Wi-Fi RTT consente la misurazione diretta della distanza da un peer Wi-Fi Aware utilizzando il suo indirizzo MAC o il suo PeerHandle.
Il rilevamento Wi-Fi Aware può essere limitato alla scoperta di servizi all'interno di un
geofence specifico. Ad esempio, puoi configurare un recinto virtuale che consenta il rilevamento
di un dispositivo che pubblica un servizio "Aware_File_Share_Service_Name"
che non
sia più vicino di 3 metri (specificati come 3000 mm) e non più lontano di 10 metri
(specificati come 10.000 mm).
Per attivare il geofencing, sia l'editore sia l'abbonato devono intervenire:
L'editore deve attivare la misurazione della distanza sul servizio pubblicato utilizzando setRangingEnabled(true).
Se l'editore non attiva la misurazione della distanza, tutti i vincoli di geofence specificati dall'abbonato vengono ignorati e viene eseguita la normale rilevazione, ignorando la distanza.
L'abbonato deve specificare un recinto virtuale utilizzando una combinazione di setMinDistanceMm e setMaxDistanceMm.
Per entrambi i valori, una distanza non specificata implica nessun limite. Se specifichi solo la distanza massima, la distanza minima è 0. Se specifichi solo la distanza minima, non è previsto un limite massimo.
Quando viene rilevato un servizio peer all'interno di un recinto virtuale, viene attivato il callback onServiceDiscoveredWithinRange, che fornisce la distanza misurata dal peer. L'API Wi-Fi RTT diretto può quindi essere chiamata in base alle necessità per misurare la distanza in un secondo momento.