Mit Wi-Fi Aware-Funktionen können Geräte mit Android 8.0 (API-Level 26) und höher andere Geräte erkennen und sich direkt mit ihnen verbinden, ohne dass eine andere Art von Verbindung zwischen ihnen besteht. 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 finden:Die API bietet einen Mechanismus, um andere Geräte in der Nähe zu finden. Der Prozess beginnt, wenn ein Gerät einen oder mehrere auffindbare Dienste veröffentlicht. Wenn ein Gerät dann einen oder mehrere Dienste abonniert und in die WLAN-Reichweite des Publishers gelangt, 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 erstellen:Nachdem zwei Geräte einander erkannt haben, können sie eine bidirektionale Wi-Fi Aware-Netzwerkverbindung ohne Zugangspunkt 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 teilen, 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 Subscriber-Discovery-Sitzung. Mit der Methode isInstantCommunicationModeSupported() kannst du feststellen, ob ein Gerät den Instant-Kommunikationsmodus unterstützt.
Verbesserungen in Android 12 (API-Level 31)
In Android 12 (API-Level 31) wurden einige Verbesserungen für Wi-Fi Aware hinzugefügt:
- 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 gefundenen 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 wurden L2-Nachrichten 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 bedeutet, dass er die MAC-Adresse des Initiators nicht im Voraus kennen muss. Dadurch wird die Bereitstellung des Datenpfads beschleunigt und mehrere Punkt-zu-Punkt-Verbindungen mit nur einer Netzwerkanfrage ermöglicht.
- Apps, die unter Android 12 oder höher ausgeführt werden, können die Methode
WifiAwareManager.getAvailableAwareResources()verwenden, um die Anzahl der derzeit verfügbaren Datenpfade, Veröffentlichungssitzungen und Abositzungen abzurufen. 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 von Wi-Fi Aware-Erkennung und -Netzwerken ein:
Fordern Sie im Manifest Ihrer App die folgenden Berechtigungen 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üfe 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, 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 die Standortermittlung 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()kannst du prüfen, ob Wi-Fi Aware derzeit verfügbar ist.Die Verfügbarkeit von Wi-Fi Aware kann sich jederzeit ändern. Deine App sollte einen
BroadcastReceiverregistrieren, umACTION_WIFI_AWARE_STATE_CHANGEDzu empfangen, der immer dann gesendet wird, wenn sich die Verfügbarkeit ändert. Wenn Ihre App den Broadcast-Intent empfängt, sollte sie alle vorhandenen Sitzungen verwerfen (unter der Annahme, 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 Sie Wi-Fi Aware verwenden können, muss Ihre App durch Aufrufen von attach() ein WifiAwareSession-Objekt abrufen. Diese Methode bewirkt Folgendes:
- 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 wurde, 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, jeweils mit einem eigenen Namespace. Dies kann in komplexen Szenarien nützlich sein, sollte aber im Allgemeinen vermieden werden.
Dienst veröffentlichen
Wenn Sie einen Dienst auffindbar machen möchten, rufen Sie die Methode publish() mit den folgenden Parametern auf:
PublishConfiggibt den Namen des Dienstes und andere Konfigurationseigenschaften an, z. B. den Abgleichsfilter.DiscoverySessionCallbackgibt die Aktionen an, die ausgeführt werden sollen, wenn Ereignisse auftreten, 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.
Wenn Geräte mit passenden Abonnenten-Apps nach der Veröffentlichung in die WLAN-Reichweite des veröffentlichenden Geräts gelangen, erkennen die Abonnenten den Dienst. Wenn ein Abonnent einen Publisher findet, 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. Sie können das Argument PeerHandle aus dieser Methode verwenden, um eine Nachricht an den Abonnenten zurückzusenden oder eine Verbindung zu ihm herzustellen.
Wenn Sie die Veröffentlichung des Dienstes beenden möchten, rufen Sie DiscoverySession.close() auf.
Discovery-Sitzungen sind mit dem übergeordneten WifiAwareSession verknüpft. Wenn die übergeordnete Sitzung geschlossen wird, werden auch die zugehörigen Erkennungssitzungen geschlossen. Verworfene Objekte werden zwar ebenfalls geschlossen, das System garantiert jedoch nicht, wann Sitzungen außerhalb des Gültigkeitsbereichs geschlossen werden. Daher empfehlen wir, die close()-Methoden explizit aufzurufen.
Dienst abonnieren
Wenn Sie einen Dienst abonnieren möchten, rufen Sie die Methode subscribe() mit den folgenden Parametern auf:
-
SubscribeConfiggibt den Namen des Dienstes an, den Sie abonnieren möchten, sowie andere Konfigurationseigenschaften, z. B. den Abgleichsfilter. DiscoverySessionCallbackgibt die Aktionen an, die ausgeführt werden sollen, wenn Ereignisse auftreten, 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 diese Referenz speichern. Sie können die Abositzung jederzeit aktualisieren, indem Sie updateSubscribe() für die Erkennungssitzung aufrufen.
An diesem Punkt wartet Ihr Abo darauf, dass übereinstimmende Publisher in die WLAN-Reichweite kommen. In diesem Fall führt das System die Callback-Methode onServiceDiscovered() aus. Sie können das Argument PeerHandle aus diesem Callback verwenden, um eine Nachricht zu senden oder eine Verbindung zu diesem Publisher herzustellen.
Wenn Sie einen Dienst nicht mehr abonnieren möchten, rufen Sie DiscoverySession.close() auf.
Discovery-Sitzungen sind mit dem übergeordneten Element WifiAwareSession verknüpft. Wenn die übergeordnete Sitzung geschlossen wird, werden auch die zugehörigen Erkennungssitzungen geschlossen. Verworfene Objekte werden zwar ebenfalls geschlossen, das System garantiert jedoch nicht, wann Sitzungen außerhalb des Gültigkeitsbereichs 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 Anwendung erhält eineDiscoverySession, indem sie entweder einen Dienst veröffentlicht oder einen Dienst abonniert.Die
PeerHandledes anderen Geräts, um die Nachricht weiterzuleiten. Ihre App erhält diePeerHandleeines anderen Geräts auf eine von zwei Arten:- Ihre Anwendung veröffentlicht einen Dienst und empfängt eine Nachricht von einem Abonnenten.
Ihre App erhält die
PeerHandledes Abonnenten über denonMessageReceived()-Callback. - Ihre App abonniert einen Dienst. Wenn dann ein passender Publisher gefunden wird, erhält Ihre App den
PeerHandledes Publishers über den CallbackonServiceDiscovered().
- Ihre Anwendung 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() an. 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 permanente Kennung von Peers verlassen. Übergeordnete Kennungen können von der Anwendung verwendet werden, die in den Discovery-Dienst selbst oder in nachfolgende Nachrichten eingebettet sind. 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 Erkennung aus, die Methode setServiceSpecificInfo() dagegen 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 paar Byte).
Verbindung herstellen
Wi-Fi Aware unterstützt Client-Server-Netzwerke 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, 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 den
ConnectivityManager, um ein Wi-Fi Aware-Netzwerk auf dem Publisher mit einemWifiAwareNetworkSpecifieranzufordern. Geben Sie dabei die Erkennungssitzung und diePeerHandledes 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 empfängt, fordert er ein Wi-Fi Aware-Netzwerk an. Dazu wird dieselbe Methode wie beim Publisher verwendet. Geben Sie beim Erstellen des
NetworkSpecifierkeinen Port an. Die entsprechenden Callback-Methoden werden aufgerufen, wenn die Netzwerkverbindung verfügbar ist, geändert wird oder verloren geht.Sobald die Methode
onAvailable()für den Abonnenten aufgerufen wurde, ist einNetwork-Objekt verfügbar, mit dem Sie einenSocketöffnen können, um mit demServerSocketauf dem Publisher zu kommunizieren. Sie müssen jedoch die IPv6-Adresse und den Port desServerSocketkennen. Diese erhalten Sie über dasNetworkCapabilities-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 nicht mehr benötigen, rufen Sie
unregisterNetworkCallback()auf.
Entfernungsmessung zu Peers 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.
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 nicht näher als 3 Meter (angegeben als 3.000 mm) und nicht weiter als 10 Meter (angegeben als 10.000 mm) entfernt ist.
Damit Geofencing aktiviert werden kann, 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 mit einer Kombination aus setMinDistanceMm und setMaxDistanceMm angeben.
Wenn Sie keinen Wert angeben, gilt kein Limit. Wenn nur die maximale Entfernung angegeben wird, wird eine Mindestentfernung von 0 angenommen. Wenn Sie nur den Mindestabstand angeben, gibt es keinen Höchstabstand.
Wenn ein Peer-Dienst innerhalb eines Geofence erkannt wird, wird der Callback onServiceDiscoveredWithinRange ausgelöst, der die gemessene Entfernung zum Peer liefert. Die direkte Wi-Fi RTT API kann dann bei Bedarf aufgerufen werden, um die Entfernung zu einem späteren Zeitpunkt zu messen.