Le funzionalità Wi-Fi Aware consentono ai dispositivi con Android 8.0 (livello API 26) e versioni successive di rilevare e connettersi direttamente l'uno all'altro, senza alcun altro tipo di connettività tra loro. La tecnologia Wi-Fi Aware è anche nota come NAN (NAN) (NAN, Resident Awareness Networking).
La rete Wi-Fi Aware funziona formando cluster con 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:
Rilevamento di altri dispositivi: l'API dispone di un meccanismo per trovare altri dispositivi nelle vicinanze. Il processo inizia quando un dispositivo pubblica uno o più servizi rilevabili. Quindi, quando un dispositivo si abbona a uno o più servizi ed entra nell'intervallo Wi-Fi dell'editore, l'abbonato riceve una notifica che informa che è stato trovato un editore corrispondente. Dopo che l'abbonato ha trovato un publisher, può inviare un breve messaggio o stabilire una connessione di rete con il dispositivo rilevato. I dispositivi possono essere contemporaneamente publisher e abbonati.
Crea una connessione di rete:dopo che due dispositivi si sono rilevati a vicenda, possono creare una connessione di rete Wi-Fi Aware bidirezionale senza punto di accesso.
Le connessioni di rete Wi-Fi Aware supportano velocità di velocità effettiva 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 utenti, ad esempio le app di condivisione di foto.
Miglioramenti ad 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 usare i metodi PublishConfig.Builder.setInstantCommunicationModeEnabled()
e SubscribeConfig.Builder.setInstantCommunicationModeEnabled()
per attivare o disattivare la modalità di comunicazione istantanea per una sessione di individuazione di publisher o abbonati. La modalità di comunicazione istantanea velocizza lo scambio di messaggi, il Service Discovery e la configurazione di qualsiasi percorso dati nell'ambito di una sessione di individuazione del publisher o degli abbonati. Per determinare se un dispositivo supporta la modalità di
comunicazione istantanea, utilizza il metodo isInstantCommunicationModeSupported()
.
Miglioramenti ad 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 di
onServiceLost()
per ricevere un avviso quando la tua app ha perso un servizio rilevato a causa dell'interruzione del servizio o del suo spostamento 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 la latenza. Sui dispositivi con Android 12 e versioni successive, il server di risposta può essere configurato in modo da accettare qualsiasi peer, vale a dire che non ha bisogno di conoscere in anticipo l'indirizzo MAC dell'iniziatore. In questo modo viene accelerato il bringup dei percorsi dati e vengono consentiti più collegamenti point-to-point con una sola richiesta di rete.
- Le app con Android 12 o versioni successive possono usare il metodo
WifiAwareManager.getAvailableAwareResources()
per ottenere il numero di percorsi di dati attualmente disponibili, le sessioni di pubblicazione e le sessioni di abbonamento. Ciò può aiutare l'app a determinare se ci sono risorse disponibili sufficienti per eseguire la funzionalità desiderata.
Configurazione iniziale
Per configurare la tua app in modo che utilizzi il rilevamento e il networking Wi-Fi Aware, procedi nel seguente modo:
Richiedi le seguenti autorizzazioni nel file manifest dell'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" />
Controlla se il dispositivo supporta Wi-Fi Aware con l'API
PackageManager
, come illustrato di seguito:Kotlin
context.packageManager.hasSystemFeature(PackageManager.FEATURE_WIFI_AWARE)
Java
context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_AWARE);
Controlla se è attualmente disponibile il Wi-Fi Aware. Il Wi-Fi Aware potrebbe essere presente sul dispositivo, ma potrebbe non essere attualmente disponibile perché l'utente ha disattivato il Wi-Fi o la geolocalizzazione. A seconda delle funzionalità hardware e firmware di alcuni dispositivi, alcuni dispositivi potrebbero non supportare il Wi-Fi Aware se sono in uso Wi-Fi Direct, SoftAP o tethering. Per controllare se la funzionalità Aware Wi-Fi è attualmente disponibile, chiama il numero
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, dovrebbe ignorare tutte le sessioni esistenti (supponendo che il servizio Wi-Fi Aware sia stato interrotto), quindi controllare lo stato attuale della disponibilità e regolare il proprio comportamento di conseguenza. Ecco alcuni esempi: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, vedi Trasmissioni.
Ottieni una sessione
Per iniziare a utilizzare Wi-Fi Aware, la tua app deve ottenere un
WifiAwareSession
chiamando il numero
attach()
. Questo metodo fa quanto segue:
- Attiva l'hardware Wi-Fi Aware.
- Si unisce o forma un cluster Wi-Fi Aware.
- 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 si collega correttamente, il sistema esegue il
callback onAttached()
.
Questo callback fornisce un oggetto WifiAwareSession
che la tua app deve utilizzare per tutte le altre operazioni relative alla sessione. Un'app può utilizzare la sessione per pubblicare un servizio o abbonarsi a un servizio.
L'app dovrebbe chiamare
attach()
una sola volta. Se
la tua app chiama attach()
più volte, l'app riceve una sessione diversa per ogni chiamata, ciascuna con il proprio
spazio dei nomi. Questa opzione potrebbe essere utile in scenari complessi, ma in genere andrebbe evitata.
Pubblica un servizio
Per rendere rilevabile un servizio, chiama il metodo publish()
, che prevede i seguenti parametri:
PublishConfig
specifica il nome del servizio e altre proprietà di configurazione, come il filtro di corrispondenza.DiscoverySessionCallback
specifica le azioni da eseguire quando si verificano eventi, ad esempio quando il sottoscrittore 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
onPublishStarted()
metodo di callback.
Dopo la pubblicazione, quando i dispositivi che eseguono app corrispondenti agli abbonati vengono spostati nell'intervallo Wi-Fi del dispositivo di pubblicazione, gli abbonati scoprono il servizio. Quando un sottoscrittore scopre un editore, quest'ultimo non riceve una notifica; tuttavia, se il sottoscrittore 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 al sottoscrittore o creare una connessione.
Per interrompere la pubblicazione del servizio, chiama
DiscoverySession.close()
.
Le sessioni di rilevamento sono associate all'elemento WifiAwareSession
principale. Se la sessione principale viene chiusa, vengono chiuse anche le sessioni di rilevamento associate. Anche se vengono chiusi gli oggetti
ignorati, il sistema non garantisce che le sessioni fuori ambito
vengano chiuse, quindi ti consigliamo di chiamare esplicitamente i metodi close()
.
Abbonarsi a un servizio
Per sottoscrivere un servizio, chiama il metodo subscribe()
, che accetta i seguenti parametri:
-
SubscribeConfig
specifica il nome del servizio a cui eseguire la sottoscrizione 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 sottoscrizione ha esito positivo, il sistema chiama il callback di onSubscribeStarted()
nella tua app. Poiché puoi utilizzare l'argomento SubscribeDiscoverySession
nel callback per comunicare con un editore dopo che l'app ne ha rilevato uno, dovresti salvare questo riferimento. Puoi aggiornare la sessione di abbonamento in qualsiasi momento
chiamando
updateSubscribe()
durante la sessione di rilevamento.
A questo punto, il tuo abbonamento attende che gli editori corrispondenti rientrino nel raggio d'azione del Wi-Fi. In questo caso, il sistema esegue il metodo di callback di onServiceDiscovered()
. Puoi utilizzare l'argomento PeerHandle
di questo callback per inviare un messaggio o creare una connessione a questo publisher.
Per interrompere l'abbonamento a un servizio, chiama
DiscoverySession.close()
.
Le sessioni di rilevamento sono associate all'elemento WifiAwareSession
principale. Se la sessione principale viene chiusa, vengono chiuse anche le sessioni di rilevamento associate. Anche se vengono chiusi gli oggetti
ignorati, il sistema non garantisce che le sessioni fuori ambito
vengano chiuse, quindi ti consigliamo di chiamare esplicitamente i metodi close()
.
Inviare un messaggio
Per inviare un messaggio a un altro dispositivo, sono necessari i seguenti oggetti:
Un
DiscoverySession
. Questo oggetto ti consente di chiamaresendMessage()
. La tua app riceve unDiscoverySession
pubblicando un servizio o abbonandoti a un servizio.PeerHandle
dell'altro dispositivo, per indirizzare il messaggio. La tua app riceve l'PeerHandle
di un altro dispositivo in uno dei due modi seguenti:- La tua app pubblica un servizio e riceve un messaggio da un abbonato.
La tua app riceve i
PeerHandle
dell'abbonato dal callbackonMessageReceived()
. - La tua app si abbona a un servizio. Poi, quando rileva un publisher corrispondente, la tua app riceve il
PeerHandle
del publisher dal callback dionServiceDiscovered()
.
- La tua app pubblica un servizio e riceve un messaggio da un abbonato.
La tua app riceve i
Per inviare un messaggio, chiama il numero
sendMessage()
. Potrebbero quindi verificarsi i seguenti callback:
- Quando il messaggio viene ricevuto correttamente dal peer, il sistema chiama il callback
onMessageSendSucceeded()
nell'app di invio. - Quando il peer riceve un messaggio, il sistema chiama il
onMessageReceived()
callback nell'app di ricezione.
Sebbene PeerHandle
sia necessario per comunicare con i peer, non dovresti
farti affidamento come identificatore permanente dei peer. L'applicazione può utilizzare identificatori di livello superiore, incorporati nel servizio di rilevamento stesso o nei messaggi successivi. Puoi incorporare un identificatore nel servizio di rilevamento con il metodo setMatchFilter()
o setServiceSpecificInfo()
di PublishConfig
o SubscribeConfig
. Il metodo setMatchFilter()
influisce sul rilevamento, mentre il metodo setServiceSpecificInfo()
non influisce sul rilevamento.
L'incorporamento di un identificatore in un messaggio implica la modifica dell'array di byte del messaggio per includere un identificatore (ad esempio, come primi due byte).
Creare 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 sottoscrivere un servizio (sul client).
Una volta che il sottoscrittore scopre l'editore, invia un messaggio all'editore.
Avvia una
ServerSocket
sul dispositivo dell'editore e imposta o recupera la porta:Kotlin
val ss = ServerSocket(0) val port = ss.localPort
Java
ServerSocket ss = new ServerSocket(0); int port = ss.getLocalPort();
Usa
ConnectivityManager
per richiedere una rete Wi-Fi Aware dell'editore utilizzando unWifiAwareNetworkSpecifier
, specificando la sessione di rilevamento e ilPeerHandle
del sottoscrittore, 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 l'editore richiede una rete, deve inviare un messaggio al sottoscrittore.
Quando il sottoscrittore riceve il messaggio dall'editore, richiedi una rete Wi-Fi Aware sul sottoscritto utilizzando lo stesso metodo utilizzato per l'editore. Non specificare una porta durante la creazione di
NetworkSpecifier
. I metodi di callback appropriati vengono chiamati quando la connessione di rete è disponibile, modificata o persa.Una volta che il metodo
onAvailable()
viene chiamato al sottoscrittore, è disponibile un oggettoNetwork
con il quale puoi aprire unSocket
per comunicare conServerSocket
sul publisher, ma devi conoscere l'indirizzo IPv6 e la porta diServerSocket
. Puoi ottenere questi valori 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()
.
Varietà di colleghi e scoperta sensibile alla posizione
Un dispositivo con funzionalità di posizione RTT Wi-Fi può misurare direttamente la distanza dai peer e utilizzare queste informazioni per limitare il rilevamento dei servizi Wi-Fi Aware.
L'API Wi-Fi RTT consente il raggio diretto a un peer Wi-Fi Aware utilizzando il suo indirizzo MAC o il suo PeerHandle.
Il rilevamento Wi-Fi Aware può essere limitato in modo da rilevare solo i servizi all'interno di un
determinato recinto virtuale. Ad esempio, puoi configurare un recinto virtuale che consente il rilevamento
di un dispositivo che pubblica un servizio "Aware_File_Share_Service_Name"
di lunghezza non superiore a 3 metri (specificata come 3000 mm) e non oltre 10 metri
(specificata come 10.000 mm).
Per attivare il geofencing, sia il publisher sia l'abbonato devono intervenire:
L'editore deve abilitare l'intervallo sul servizio pubblicato utilizzando setRangingEnabled(true).
Se il publisher non abilita il raggio d'azione, tutti i vincoli del recinto virtuale specificati dal sottoscrittore vengono ignorati e viene eseguito il normale rilevamento, 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 non implica alcun limite. Specificare solo la distanza massima implica una distanza minima pari a 0. Specificare solo la distanza minima non implica alcun limite massimo.
Quando viene rilevato un servizio peer all'interno di un recinto virtuale, viene attivato il callback onServiceDiscovered WithinRange che fornisce la distanza misurata dal peer. L'API Direct Wi-Fi RTT può quindi essere chiamata in base alle esigenze per misurare la distanza in un secondo momento.