Mit den Wi-Fi Aware-Funktionen können Geräte mit Android 8.0 (API-Level 26) und höher andere Geräte direkt erkennen und sich direkt mit ihnen verbinden, ohne dass eine andere Art von Verbindung zwischen ihnen erforderlich ist. Wi-Fi Aware wird auch als Neighbor Awareness Networking (NAN) bezeichnet.
Bei der Wi-Fi Aware-Vernetzung werden Cluster mit Geräten in der Nähe gebildet oder ein neuer Cluster erstellt, 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 Vorgang 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 eine bidirektionale Wi-Fi Aware-Netzwerkverbindung ohne Zugriffspunkt 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 Modus für die sofortige
Kommunikation unterstützen, können Apps die Methoden
PublishConfig.Builder.setInstantCommunicationModeEnabled() und
SubscribeConfig.Builder.setInstantCommunicationModeEnabled() verwenden, um den Modus für die sofortige Kommunikation für eine Publisher- oder Abonnenten
Erkennungssitzung zu
aktivieren oder zu deaktivieren. Der Modus für die sofortige Kommunikation beschleunigt den Nachrichtenaustausch, die Service Discovery und die Einrichtung von Datenpfaden im Rahmen einer Publisher- oder Abonnenten-Erkennungssitzung. Verwenden Sie die Methode isInstantCommunicationModeSupported(), um zu ermitteln, ob ein Gerät den Modus für die sofortige Kommunikation
unterstützt.
Verbesserungen in Android 12 (API-Level 31)
In Android 12 (API-Level 31) wurden einige Verbesserungen an Wi-Fi Aware vorgenommen:
- Auf Geräten mit Android 12 (API-Level 31) oder höher können Sie mit dem
onServiceLost()Callback benachrichtigt werden, wenn Ihre App einen erkannten Dienst verloren hat, weil der Dienst beendet wurde oder sich außerhalb der Reichweite befindet. - Die Einrichtung von Wi-Fi Aware-Datenpfaden wurde vereinfacht. In früheren Versionen wurde L2-Messaging verwendet, um die MAC-Adresse des Initiators bereitzustellen, was zu Latenz führte. Auf Geräten mit Android 12 und höher kann der Responder (Server) so konfiguriert werden, dass er jeden Peer akzeptiert. Das heißt, er muss die MAC-Adresse des Initiators nicht im Voraus kennen. Dadurch wird die Einrichtung des Datenpfads beschleunigt und es können mehrere Punkt-zu-Punkt-Verbindungen mit nur einer Netzwerkanfrage hergestellt werden.
- Apps, die unter Android 12 oder höher ausgeführt werden, können mit der
WifiAwareManager.getAvailableAwareResources()Methode die Anzahl der derzeit verfügbaren Datenpfade, Veröffentlichungssitzungen und Abositzungen abrufen. So kann die App feststellen, ob genügend Ressourcen verfügbar sind, um die gewünschte Funktion auszuführen.
Ersteinrichtung
So richten Sie Ihre App für die Verwendung der Wi-Fi Aware-Erkennung und -Vernetzung 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
PackageManagerAPI, 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, aber derzeit nicht verfügbar, weil der Nutzer WLAN oder den Standort deaktiviert hat. Je nach Hardware- und Firmware-Funktionen unterstützen einige Geräte Wi-Fi Aware möglicherweise nicht, wenn Wi-Fi Direct, SoftAP oder Tethering verwendet wird. Rufen Sie
isAvailable()auf, um zu 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
BroadcastReceiverregistrieren, umACTION_WIFI_AWARE_STATE_CHANGEDzu empfangen. Diese Broadcast-Intent wird gesendet, wenn sich die Verfügbarkeit ändert. Wenn Ihre App die Broadcast-Intent empfängt, sollte sie alle vorhandenen Sitzungen verwerfen (davon ausgehen, dass der Wi-Fi Aware-Dienst unterbrochen wurde), 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 eine WifiAwareSession abrufen, indem sie attach() aufruft. Diese Methode führt Folgendes 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 Erkennungssitzungen dient.
Wenn die App erfolgreich angehängt wird, führt das System den Callback onAttached() 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. Das kann in komplexen Szenarien nützlich sein, sollte aber im Allgemeinen vermieden werden.
Dienst veröffentlichen
Rufen Sie die Methode publish() auf, um einen Dienst erkennbar zu machen. Diese Methode verwendet die folgenden Parameter:
PublishConfiggibt den Namen des Dienstes und andere Konfigurationseigenschaften an, z. B. den Abgleichsfilter.DiscoverySessionCallbackgibt die Aktionen an, die ausgeführt werden sollen, wenn Ereignisse eintreten, z. B. wenn der Abonnent eine Nachricht erhält.
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 erkennen Abonnenten den Dienst, wenn sich Geräte mit passenden Abonnenten-Apps im WLAN-Bereich des veröffentlichenden Geräts befinden. Wenn ein Abonnent einen Publisher erkennt, 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 aus dieser Methode können Sie
eine Nachricht an den Abonnenten zurücksenden oder
eine Verbindung zu ihm herstellen.
Rufen Sie DiscoverySession.close() auf, um die Veröffentlichung des Dienstes zu beenden.
Erkennungssitzungen sind mit ihrer übergeordneten WifiAwareSession verknüpft. Wenn die übergeordnete Sitzung geschlossen wird, werden auch die zugehörigen Erkennungssitzungen geschlossen. Verworfene Objekte werden ebenfalls geschlossen. Das System garantiert jedoch nicht, wann Sitzungen außerhalb des Gültigkeitsbereichs geschlossen werden. Daher empfehlen wir, die Methoden close() explizit aufzurufen.
Dienst abonnieren
Rufen Sie die Methode subscribe() auf, um einen Dienst zu abonnieren. Diese Methode verwendet die folgenden Parameter:
-
SubscribeConfiggibt den Namen des zu abonnierenden Dienstes und andere Konfigurationseigenschaften an, z. B. den Abgleichs filter. DiscoverySessionCallbackgibt 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 Abovorgang erfolgreich ist, ruft das System den Callback onSubscribeStarted() in Ihrer App auf. Da Sie das Argument SubscribeDiscoverySession 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.
Ihr Abo wartet nun darauf, dass passende Publisher in den WLAN-Bereich kommen. In diesem Fall führt das System die
onServiceDiscovered()
Callback-Methode aus. Mit dem PeerHandle
Argument aus diesem Callback können Sie eine Nachricht senden oder
eine Verbindung herstellen zu diesem Publisher.
Rufen Sie DiscoverySession.close() auf, um das Abo eines Dienstes zu beenden.
Erkennungssitzungen sind mit ihrer übergeordneten WifiAwareSession verknüpft. Wenn die übergeordnete Sitzung geschlossen wird, werden auch die zugehörigen Erkennungssitzungen geschlossen. Verworfene Objekte werden ebenfalls geschlossen. Das System garantiert jedoch nicht, wann Sitzungen außerhalb des Gültigkeitsbereichs geschlossen werden. Daher empfehlen wir, die Methoden close() explizit aufzurufen.
Nachricht senden
Um eine Nachricht an ein anderes Gerät zu senden, benötigen Sie die folgenden Objekte:
Eine
DiscoverySession. Mit diesem Objekt können SiesendMessage()aufrufen. Ihre App erhält eineDiscoverySession, indem sie entweder einen Dienst veröffentlicht oder einen Dienst abonniert.PeerHandledes anderen Geräts, um die Nachricht weiterzuleiten. Ihre App erhält diePeerHandleeines anderen Geräts auf eine von zwei Arten:- Ihre App veröffentlicht einen Dienst und erhält eine Nachricht von einem Abonnenten.
Ihre App erhält die
PeerHandledes Abonnenten über den CallbackonMessageReceived(). - Ihre App abonniert einen Dienst. Wenn sie dann einen passenden
Publisher erkennt, erhält Ihre App die
PeerHandledes Publishers über denonServiceDiscovered()Callback.
- Ihre App veröffentlicht einen Dienst und erhält eine Nachricht von einem Abonnenten.
Ihre App erhält die
Rufen Sie sendMessage() auf, um eine Nachricht zu senden. Die folgenden Callbacks können dann auftreten:
- Wenn die Nachricht erfolgreich vom Peer empfangen wurde, ruft das System den Callback
onMessageSendSucceeded()in der sendenden App auf. - Wenn der Peer eine Nachricht empfängt, ruft das System den Callback
onMessageReceived()in der empfangenden App auf.
Obwohl die PeerHandle für die Kommunikation mit Peers erforderlich ist, sollten Sie sich nicht darauf als dauerhafte Kennung von Peers verlassen. Höherwertige Kennungen können von der Anwendung verwendet werden – eingebettet im Erkennungsdienst selbst oder in nachfolgenden Nachrichten. Sie können eine Kennung mit
der
setMatchFilter()
oder
setServiceSpecificInfo()
Methode von PublishConfig oder
SubscribeConfig in den Erkennungsdienst einbetten. Die Methode setMatchFilter() wirkt sich auf die Erkennung aus, während die Methode setServiceSpecificInfo() die Erkennung nicht beeinflusst.
Das Einbetten einer Kennung in eine Nachricht bedeutet, dass das Byte-Array der Nachricht so geändert wird, dass es eine Kennung enthält (z. B. als erste Byte).
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 erkennt, senden Sie eine Nachricht vom Abonnenten an den Publisher.
Starten Sie einen
ServerSocketauf dem Publisher Gerät 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
ConnectivityManagerzum Anfordern eines Wi-Fi Aware-Netzwerks auf dem Publisher. Verwenden Sie dazu einenWifiAwareNetworkSpecifierund geben Sie die Erkennungssitzung und diePeerHandledes Abonnenten an, die Sie aus der vom Abonnenten gesendeten 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 erhält, fordern Sie ein Wi-Fi Aware-Netzwerk auf dem Abonnenten mit derselben Methode wie auf dem Publisher an. Geben Sie beim Erstellen des
NetworkSpecifierkeinen Port an. Die entsprechenden Callback-Methoden werden aufgerufen, wenn die Netzwerkverbindung verfügbar ist, sich ändert oder verloren geht.Sobald die
onAvailable()Methode auf dem Abonnenten aufgerufen wird, ist einNetworkObjekt verfügbar, mit dem Sie einSocketöffnen können, um mit derServerSocketauf dem Publisher zu kommunizieren. Sie benötigen jedoch zwingend dieServerSocket’s IPv6-Adresse und den Port. Sie erhalten diese aus demNetworkCapabilitiesObjekt 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 nicht mehr benötigen, rufen Sie
unregisterNetworkCallback()auf.
Peers in Reichweite und standortbezogene Erkennung
Ein Gerät mit Wi-Fi RTT-Standortfunktionen kann die Entfernung zu Peers direkt messen und diese Informationen verwenden, um die Wi-Fi Aware-Service Discovery einzuschränken.
Mit der Wi-Fi RTT API kann die Entfernung zu einem Wi-Fi Aware-Peer direkt gemessen werden. Dazu wird entweder die MAC-Adresse oder die PeerHandle verwendet.
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.
Um Geofencing zu aktivieren, müssen sowohl der Publisher als auch der Abonnent Maßnahmen ergreifen:
Der Publisher muss die Entfernungsmessung für den veröffentlichten Dienst mit setRangingEnabled(true) aktivieren.
Wenn der Publisher die Entfernungsmessung nicht aktiviert, werden alle vom Abonnenten angegebenen Geofence-Einschränkungen ignoriert und die normale Erkennung wird ohne Berücksichtigung der Entfernung durchgeführt.
Der Abonnent muss einen Geofence angeben, indem er eine Kombination aus setMinDistanceMm und setMaxDistanceMmverwendet.
Für beide Werte bedeutet eine nicht angegebene Entfernung, dass es kein Limit gibt. Wenn nur die maximale Entfernung angegeben wird, beträgt die Mindestentfernung 0. Wenn nur die Mindestentfernung angegeben wird, gibt es keine maximale Entfernung.
Wenn ein Peer-Dienst innerhalb eines Geofence erkannt wird, wird der onServiceDiscoveredWithinRange Callback ausgelöst, der die gemessene Entfernung zum Peer angibt. Die direkte Wi-Fi RTT API kann dann bei Bedarf aufgerufen werden, um die Entfernung zu einem späteren Zeitpunkt zu messen.