Mit Wi-Fi Aware-Funktionen können Geräte mit Android 8.0 (API-Level 26) und höher sich gegenseitig erkennen und direkt miteinander verbinden, ohne dass eine andere Art von Verbindung zwischen ihnen erforderlich ist. Wi‑Fi Aware wird auch als Neighbor Awareness Networking (NAN) bezeichnet.
Wi‑Fi Aware-Netzwerke funktionieren, indem Cluster mit benachbarten Geräten gebildet werden oder ein neuer Cluster erstellt wird, wenn das Gerät das erste in einem Bereich ist. Dieses Clustering-Verhalten gilt für das gesamte Gerät und wird vom Wi-Fi Aware-Systemdienst verwaltet. Apps haben keine Kontrolle über das Clustering-Verhalten. Apps verwenden die Wi-Fi Aware-APIs, um mit dem Wi-Fi Aware-Systemdienst zu kommunizieren, der die Wi-Fi Aware-Hardware auf dem Gerät verwaltet.
Mit den Wi-Fi Aware-APIs können Apps die folgenden Vorgänge ausführen:
Andere Geräte erkennen:Die API bietet einen Mechanismus zum Auffinden anderer Geräte in der Nähe. Der Prozess beginnt, wenn ein Gerät einen oder mehrere erkennbare Dienste veröffentlicht. Wenn ein Gerät dann einen oder mehrere Dienste abonniert und sich im WLAN-Bereich des Publishers befindet, erhält der Abonnent eine Benachrichtigung, dass ein passender Publisher gefunden wurde. Nachdem der Abonnent einen Publisher gefunden hat, kann er entweder eine kurze Nachricht senden oder eine Netzwerkverbindung mit dem gefundenen Gerät herstellen. Geräte können gleichzeitig Publisher und Abonnenten sein.
Netzwerkverbindung herstellen:Nachdem zwei Geräte sich gegenseitig erkannt haben, können sie ohne Access Point eine bidirektionale Wi-Fi Aware-Netzwerkverbindung herstellen.
Wi-Fi Aware-Netzwerkverbindungen unterstützen höhere Durchsatzraten über größere Entfernungen als Bluetooth-Verbindungen. Diese Arten von Verbindungen sind nützlich für Apps, die große Datenmengen zwischen Nutzern austauschen, z. B. Apps zum Teilen von Fotos.
Verbesserungen in Android 13 (API-Level 33)
Auf Geräten mit Android 13 (API‑Level 33) und höher, die den Sofortkommunikationsmodus unterstützen, können Apps die Methoden PublishConfig.Builder.setInstantCommunicationModeEnabled()
und SubscribeConfig.Builder.setInstantCommunicationModeEnabled()
verwenden, um den Sofortkommunikationsmodus für eine Publisher- oder Subscriber-Erkennungssitzung zu aktivieren oder zu deaktivieren. Der Modus für die sofortige Kommunikation beschleunigt den Nachrichtenaustausch, die Dienstermittlung und die Einrichtung von Datenpfaden im Rahmen einer Publisher- oder Subscriber-Ermittlungssitzung. Verwenden Sie die Methode isInstantCommunicationModeSupported()
, um festzustellen, ob ein Gerät den Modus für die sofortige Kommunikation unterstützt.
Verbesserungen für Android 12 (API-Level 31)
Unter Android 12 (API-Level 31) gibt es einige Verbesserungen für Wi-Fi Aware:
- Auf Geräten mit Android 12 (API-Level 31) oder höher können Sie den Callback
onServiceLost()
verwenden, um benachrichtigt zu werden, wenn Ihre App einen erkannten Dienst verliert, weil der Dienst beendet wird oder sich außerhalb der Reichweite befindet. - Die Einrichtung von Wi-Fi Aware-Datenpfaden wurde vereinfacht. In früheren Versionen wurde die MAC-Adresse des Initiators über L2-Nachrichten bereitgestellt, was zu Latenz führte. Auf Geräten mit Android 12 und höher kann der Responder (Server) so konfiguriert werden, dass er alle Peers akzeptiert. Das heißt, er muss die MAC-Adresse des Initiators nicht im Voraus kennen. Dadurch wird die Einrichtung des Datenpfads beschleunigt und es sind mehrere Punkt-zu-Punkt-Verbindungen mit nur einer Netzwerkanfrage möglich.
- Apps, die unter Android 12 oder höher ausgeführt werden, können mit der Methode
WifiAwareManager.getAvailableAwareResources()
die Anzahl der derzeit verfügbaren Datenpfade abrufen, Sitzungen veröffentlichen und Sitzungen abonnieren. So kann die App feststellen, ob genügend Ressourcen für die Ausführung der gewünschten Funktion verfügbar sind.
Ersteinrichtung
So richten Sie Ihre App für die Verwendung von Wi-Fi Aware-Erkennung und ‑Netzwerken ein:
Fordern Sie die folgenden Berechtigungen im Manifest Ihrer App an:
<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" />
Prüfen Sie mit der
PackageManager
API, ob das Gerät Wi-Fi Aware unterstützt, wie unten gezeigt:Kotlin
context.packageManager.hasSystemFeature(PackageManager.FEATURE_WIFI_AWARE)
Java
context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_AWARE);
Prüfen Sie, ob Wi-Fi Aware derzeit verfügbar ist. Wi-Fi Aware ist möglicherweise auf dem Gerät vorhanden, ist aber derzeit nicht verfügbar, da der Nutzer WLAN oder den Standort deaktiviert hat. Je nach Hardware- und Firmwarefunktionen unterstützen einige Geräte Wi‑Fi Aware möglicherweise nicht, wenn Wi‑Fi Direct, SoftAP oder Tethering verwendet wird. Mit
isAvailable()
können Sie prüfen, ob Wi-Fi Aware derzeit verfügbar ist.Die Verfügbarkeit von Wi‑Fi Aware kann sich jederzeit ändern. Ihre App sollte einen
BroadcastReceiver
registrieren, umACTION_WIFI_AWARE_STATE_CHANGED
zu empfangen, die bei jeder Änderung der Verfügbarkeit gesendet wird. Wenn Ihre App den Broadcast-Intent empfängt, sollte sie alle vorhandenen Sitzungen verwerfen (davon ausgehen, dass der Wi-Fi Aware-Dienst unterbrochen wurde), dann den aktuellen Verfügbarkeitsstatus prüfen und ihr Verhalten entsprechend anpassen. Beispiel: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);
Weitere Informationen finden Sie unter Broadcasts.
Sitzung abrufen
Damit Ihre App Wi-Fi Aware verwenden kann, muss sie durch Aufrufen von attach()
ein WifiAwareSession
abrufen. Diese Methode führt folgende Schritte aus:
- Aktiviert die Wi‑Fi Aware-Hardware.
- Tritt einem Wi-Fi Aware-Cluster bei oder bildet einen.
- Erstellt eine Wi-Fi Aware-Sitzung mit einem eindeutigen Namespace, der als Container für alle darin erstellten Discovery-Sitzungen dient.
Wenn die App erfolgreich angehängt wird, führt das System den onAttached()
-Callback aus.
Dieser Callback stellt ein WifiAwareSession
-Objekt bereit, das Ihre App für alle weiteren Sitzungsvorgänge verwenden sollte. Eine App kann die Sitzung verwenden, um einen Dienst zu veröffentlichen oder einen Dienst zu abonnieren.
Ihre App sollte attach()
nur einmal aufrufen. Wenn Ihre App attach()
mehrmals aufruft, erhält sie für jeden Aufruf eine andere Sitzung mit einem eigenen Namespace. Dies kann in komplexen Szenarien nützlich sein, sollte aber im Allgemeinen vermieden werden.
Dienst veröffentlichen
Damit ein Dienst erkannt werden kann, rufen Sie die Methode publish()
auf. Sie verwendet die folgenden Parameter:
PublishConfig
gibt den Namen des Dienstes und andere Konfigurationseigenschaften wie den Abgleichsfilter an.DiscoverySessionCallback
gibt die Aktionen an, die ausgeführt werden sollen, wenn Ereignisse eintreten, z. B. wenn der Abonnent eine Nachricht empfängt.
Beispiel:
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);
Wenn die Veröffentlichung erfolgreich ist, wird die Callback-Methode onPublishStarted()
aufgerufen.
Nach der Veröffentlichung wird der Dienst von Abonnenten entdeckt, wenn Geräte mit entsprechenden Abonnenten-Apps in den WLAN-Bereich des veröffentlichenden Geräts gelangen. Wenn ein Abonnent einen Publisher entdeckt, erhält der Publisher keine Benachrichtigung. Wenn der Abonnent jedoch eine Nachricht an den Publisher sendet, erhält der Publisher eine Benachrichtigung. In diesem Fall wird die Callback-Methode onMessageReceived()
aufgerufen. Mit dem PeerHandle
-Argument dieser Methode können Sie eine Nachricht an den Abonnenten senden oder eine Verbindung zu ihm herstellen.
Rufen Sie DiscoverySession.close()
auf, um die Veröffentlichung des Dienstes zu beenden.
Discovery-Sitzungen sind mit dem übergeordneten WifiAwareSession
verknüpft. Wenn die übergeordnete Sitzung geschlossen wird, werden auch die zugehörigen Erkundungssitzungen geschlossen. Verworfene Objekte werden zwar auch geschlossen, das System garantiert jedoch nicht, wann Sessions, die nicht in den Umfang fallen, geschlossen werden. Daher empfehlen wir, die close()
-Methoden explizit aufzurufen.
Dienst abonnieren
Wenn Sie einen Dienst abonnieren möchten, rufen Sie die Methode subscribe()
auf. Sie verwendet die folgenden Parameter:
-
SubscribeConfig
gibt den Namen des Dienstes an, für den ein Abo abgeschlossen werden soll, sowie andere Konfigurationseigenschaften wie den Abgleichsfilter. DiscoverySessionCallback
gibt die Aktionen an, die ausgeführt werden sollen, wenn Ereignisse eintreten, z. B. wenn ein Publisher erkannt wird.
Beispiel:
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);
Wenn der Vorgang zum Abonnieren erfolgreich ist, ruft das System den onSubscribeStarted()
-Callback in Ihrer App auf. Da Sie das SubscribeDiscoverySession
-Argument im Callback verwenden können, um mit einem Publisher zu kommunizieren, nachdem Ihre App einen gefunden hat, sollten Sie diesen Verweis speichern. Sie können die Abositzung jederzeit aktualisieren, indem Sie updateSubscribe()
in der Erkennungssitzung aufrufen.
Jetzt wartet Ihr Abo darauf, dass passende Publisher in WLAN-Reichweite kommen. In diesem Fall führt das System die Callback-Methode onServiceDiscovered()
aus. Mit dem PeerHandle
-Argument aus diesem Callback können Sie eine Nachricht senden oder eine Verbindung zu diesem Publisher herstellen.
Rufen Sie DiscoverySession.close()
auf, um ein Abo für einen Dienst zu beenden.
Discovery-Sitzungen sind mit dem übergeordneten WifiAwareSession
verknüpft. Wenn die übergeordnete Sitzung geschlossen wird, werden auch die zugehörigen Erkundungssitzungen geschlossen. Verworfene Objekte werden zwar auch geschlossen, das System garantiert jedoch nicht, wann Sessions, die nicht in den Umfang fallen, geschlossen werden. Daher empfehlen wir, die close()
-Methoden explizit aufzurufen.
Nachricht senden
Wenn Sie eine Nachricht an ein anderes Gerät senden möchten, benötigen Sie die folgenden Objekte:
A
DiscoverySession
. Mit diesem Objekt können SiesendMessage()
aufrufen. Ihre App erhält eineDiscoverySession
, indem Sie entweder einen Dienst veröffentlichen oder einen Dienst abonnieren.Die
PeerHandle
des anderen Geräts, um die Nachricht weiterzuleiten. Es gibt zwei Möglichkeiten, wie Ihre App diePeerHandle
eines anderen Geräts erhält:- Ihre App veröffentlicht einen Dienst und empfängt eine Nachricht von einem Abonnenten.
Ihre App erhält die
PeerHandle
des Abonnenten über denonMessageReceived()
-Callback. - Ihre App abonniert einen Dienst. Wenn ein passender Publisher gefunden wird, erhält Ihre App die
PeerHandle
des Publishers über denonServiceDiscovered()
-Callback.
- Ihre App veröffentlicht einen Dienst und empfängt eine Nachricht von einem Abonnenten.
Ihre App erhält die
Wenn Sie eine Nachricht senden möchten, rufen Sie sendMessage()
auf. Die folgenden Callbacks können dann auftreten:
- Wenn die Nachricht vom Peer empfangen wurde, ruft das System den
onMessageSendSucceeded()
-Callback in der sendenden App auf. - Wenn der Peer eine Nachricht empfängt, ruft das System den
onMessageReceived()
-Callback in der empfangenden App auf.
Die PeerHandle
ist zwar für die Kommunikation mit Peers erforderlich, sollte aber nicht als dauerhafte Kennung von Peers verwendet werden. Kennungen auf höherer Ebene können von der Anwendung verwendet werden – eingebettet in den Erkennungsdienst selbst oder in nachfolgenden Nachrichten. Sie können eine Kennung mit der Methode setMatchFilter()
oder setServiceSpecificInfo()
von PublishConfig
oder SubscribeConfig
in den Discovery-Dienst einbetten. Die Methode setMatchFilter()
wirkt sich auf die Sichtbarkeit aus, die Methode setServiceSpecificInfo()
hingegen nicht.
Wenn Sie eine Kennung in eine Nachricht einbetten, müssen Sie das Byte-Array der Nachricht so ändern, dass es eine Kennung enthält (z. B. als die ersten Bytes).
Verbindung herstellen
Wi-Fi Aware unterstützt die Client-Server-Vernetzung zwischen zwei Wi-Fi Aware-Geräten.
So richten Sie die Client-Server-Verbindung ein:
Verwenden Sie die Wi-Fi Aware-Erkennung, um einen Dienst zu veröffentlichen (auf dem Server) und einen Dienst zu abonnieren (auf dem Client).
Sobald der Abonnent den Publisher gefunden hat, sendet er eine Nachricht an den Publisher.
Starten Sie ein
ServerSocket
auf dem Gerät des Publishers und legen Sie den Port fest oder rufen Sie ihn ab:Kotlin
val ss = ServerSocket(0) val port = ss.localPort
Java
ServerSocket ss = new ServerSocket(0); int port = ss.getLocalPort();
Verwenden Sie die
ConnectivityManager
, um ein Wi-Fi Aware-Netzwerk auf dem Publisher mit einemWifiAwareNetworkSpecifier
anzufordern. Geben Sie dabei die Discovery-Sitzung und diePeerHandle
des Abonnenten an, die Sie aus der vom Abonnenten übertragenen Nachricht erhalten haben: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);
Sobald der Publisher ein Netzwerk anfordert, sollte er eine Nachricht an den Abonnenten senden.
Sobald der Abonnent die Nachricht vom Publisher erhalten hat, fordern Sie auf dem Abonnenten ein Wi-Fi Aware-Netzwerk an. Verwenden Sie dazu dieselbe Methode wie auf dem Publisher. Geben Sie beim Erstellen des
NetworkSpecifier
keinen Port an. Die entsprechenden Callback-Methoden werden aufgerufen, wenn die Netzwerkverbindung verfügbar ist, sich ändert oder verloren geht.Sobald die
onAvailable()
-Methode für den Abonnenten aufgerufen wird, ist einNetwork
-Objekt verfügbar, mit dem Sie eineSocket
öffnen können, um mit demServerSocket
auf dem Publisher zu kommunizieren. Dazu müssen Sie jedoch die IPv6-Adresse und den Port desServerSocket
kennen. Sie erhalten diese aus demNetworkCapabilities
-Objekt, das imonCapabilitiesChanged()
-Callback bereitgestellt wird: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);
Wenn Sie die Netzwerkverbindung beenden möchten, rufen Sie
unregisterNetworkCallback()
auf.
Entfernungsmessung zu Peers und standortbezogene Suche
Ein Gerät mit Wi-Fi RTT-Standort-Funktionen kann die Entfernung zu Peers direkt messen und diese Informationen verwenden, um die Wi-Fi Aware-Dienstermittlung einzuschränken.
Die Wi-Fi RTT API ermöglicht die direkte Entfernungsmessung zu einem Wi-Fi Aware-Peer über seine MAC-Adresse oder sein PeerHandle.
Die Wi-Fi Aware-Erkennung kann so eingeschränkt werden, dass nur Dienste innerhalb eines bestimmten Geofence erkannt werden. Sie können beispielsweise einen Geofence einrichten, der die Erkennung eines Geräts ermöglicht,das einen "Aware_File_Share_Service_Name"
-Dienst veröffentlicht,der sich in einer Entfernung von mindestens 3 Metern (angegeben als 3.000 mm) und höchstens 10 Metern (angegeben als 10.000 mm) befindet.
Damit Geofencing aktiviert werden kann, müssen sowohl der Verlag/Webpublisher als auch der Abonnent aktiv werden:
Der Verlag oder Webpublisher muss die Bereichsbestimmung für den veröffentlichten Dienst mit setRangingEnabled(true) aktivieren.
Wenn der Publisher keine Bereichsangaben aktiviert, werden alle vom Abonnenten angegebenen Geofence-Einschränkungen ignoriert und die normale Suche wird ohne Berücksichtigung der Entfernung durchgeführt.
Der Abonnent muss einen Geofence mit einer Kombination aus setMinDistanceMm und setMaxDistanceMm angeben.
Wenn für einen der beiden Werte keine Entfernung angegeben ist, gibt es kein Limit. Wenn Sie nur den maximalen Abstand angeben, wird ein Mindestabstand von 0 angenommen. Wenn Sie nur die Mindestentfernung angeben, gibt es kein Maximum.
Wenn ein Peer-Dienst innerhalb eines Geofence erkannt wird, wird der onServiceDiscoveredWithinRange-Callback ausgelöst, der die gemessene Entfernung zum Peer angibt. Die Direct Wi‑Fi RTT API kann dann bei Bedarf aufgerufen werden, um die Entfernung zu einem späteren Zeitpunkt zu messen.