Dank Wi-Fi-Aware-Funktionen können Geräte mit Android 8.0 (API-Level 26) und höher sich gegenseitig erkennen und direkt eine Verbindung herstellen, ohne dass eine andere Art von Verbindung zwischen ihnen besteht. Wi-Fi Aware ist auch als Neighbor Awareness Networking (Neighbor Awareness Networking) bekannt.
In WLAN-sensitiven Netzwerken werden Cluster mit benachbarten Geräten 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 folgende Vorgänge ausführen:
Andere Geräte finden:Die API bietet einen Mechanismus, um 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 den WLAN-Bereich des Verlags oder Webpublishers gelangt, erhält der Abonnent eine Benachrichtigung, dass ein übereinstimmender Publisher gefunden wurde. Nachdem der Abonnent einen Publisher entdeckt hat, kann der Abonnent entweder eine kurze Nachricht senden oder eine Netzwerkverbindung mit dem erkannten 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 über größere Entfernungen höhere Durchsatzraten als Bluetooth-Verbindungen. Diese Verbindungstypen sind nützlich für Apps, die große Datenmengen unter Nutzern teilen, z. B. Apps zum Teilen von Fotos.
Verbesserungen für Android 12 (API-Level 31)
Unter Android 12 (API-Level 31) wurden einige Verbesserungen bei Wi-Fi Aware vorgenommen:
- Auf Geräten mit Android 12 (API-Level 31) oder höher kannst du den
onServiceLost()
-Callback verwenden, um dich zu benachrichtigen, wenn deine App einen erkannten Dienst verloren hat, weil der Dienst angehalten wurde oder sich außerhalb des Bereichs bewegt. - 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 Latenz verursachte. Auf Geräten mit Android 12 und höher kann der Listener (Server) so konfiguriert werden, dass er beliebige Peers akzeptiert. Das heißt, er muss die MAC-Adresse des Initators nicht im Voraus kennen. Dies beschleunigt das Hochladen des Datenpfads und ermöglicht mehrere Punkt-zu-Punkt-Links mit nur einer Netzwerkanfrage.
- Apps, die unter Android 12 oder höher ausgeführt werden, können mit der Methode
WifiAwareManager.getAvailableAwareResources()
die Anzahl der aktuell verfügbaren Datenpfade sowie Veröffentlichungs- und Abositzungen abrufen. So kann die Anwendung feststellen, ob genügend Ressourcen für die Ausführung der gewünschten Funktion vorhanden sind.
Ersteinrichtung
Führe die folgenden Schritte aus, um deine App für die WLAN-Erkennung und -Netzwerke einzurichten:
Fordere im Manifest deiner 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, ob das Gerät Wi-Fi Aware mit der
PackageManager
API unterstützt: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. Möglicherweise ist Wi-Fi-Aware auf dem Gerät vorhanden, ist aber derzeit nicht verfügbar, weil der Nutzer WLAN oder den Standort deaktiviert hat. Je nach Hardware- und Firmwarefunktionen unterstützen einige Geräte Wi-Fi Direct, SoftAP oder Tethering möglicherweise nicht. Wenn du prüfen möchtest, ob Wi-Fi Aware derzeit verfügbar ist, ruf
isAvailable()
an.Die Verfügbarkeit von Wi-Fi Aware kann sich jederzeit ändern. Ihre Anwendung sollte eine
BroadcastReceiver
registrieren, umACTION_WIFI_AWARE_STATE_CHANGED
zu erhalten, die gesendet wird, wenn sich die Verfügbarkeit ändert. Wenn Ihre App den Broadcast-Intent empfängt, sollten alle vorhandenen Sitzungen verworfen werden (unter der Annahme, dass der Wi-Fi Aware-Dienst unterbrochen wurde). Anschließend sollte der aktuelle Verfügbarkeitsstatus geprüft und das Verhalten entsprechend angepasst werden. Beispiele: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 erhalten
Damit du Wi-Fi Aware nutzen kannst, muss deine App durch Aufrufen von attach()
ein WifiAwareSession
anfordern. Diese Methode führt folgende Schritte aus:
- Aktiviert Wi-Fi Aware-Hardware.
- Stellt einem Wi-Fi Aware-Cluster bei oder bildet einen solchen Cluster.
- Erstellt eine Wi-Fi Aware-Sitzung mit einem eindeutigen Namespace, der als Container für alle darin erstellten Erkennungssitzungen fungiert.
Wenn die Anwendung erfolgreich angehängt wurde, führt das System den onAttached()
-Callback aus.
Dieser Callback stellt ein WifiAwareSession
-Objekt bereit, das deine App für alle weiteren Sitzungsvorgänge verwenden soll. Über die Sitzung kann eine Anwendung einen Dienst veröffentlichen oder einen Dienst abonnieren.
Deine App sollte attach()
nur einmal aufrufen. Wenn Ihre Anwendung attach()
mehrmals aufruft, erhält sie für jeden Aufruf eine andere Sitzung mit jeweils einem eigenen Namespace. Dies kann bei 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()
auf, die die folgenden Parameter verwendet:
PublishConfig
gibt den Namen des Dienstes und andere Konfigurationsattribute wie Übereinstimmungsfilter an.DiscoverySessionCallback
gibt die Aktionen an, die bei Ereignissen ausgeführt werden sollen, 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);
Bei erfolgreicher Veröffentlichung wird die Callback-Methode onPublishStarted()
aufgerufen.
Wenn sich nach der Veröffentlichung Geräte, auf denen passende Abonnenten-Apps ausgeführt werden, in die WLAN-Reichweite des Veröffentlichungsgeräts bewegen, entdecken Abonnenten den Dienst. Wenn ein Abonnent einen Verlag oder Webpublisher entdeckt, erhält dieser keine Benachrichtigung. Wenn der Abonnent jedoch eine Nachricht an den Verlag oder Webpublisher sendet, erhält dieser eine Benachrichtigung. In diesem Fall wird die Callback-Methode onMessageReceived()
aufgerufen. Mit dem Argument PeerHandle
dieser Methode können Sie eine Nachricht an den Abonnenten zurücksenden oder eine Verbindung zu ihr erstellen.
Rufen Sie DiscoverySession.close()
auf, um die Veröffentlichung des Dienstes zu beenden.
Discovery-Sitzungen sind der übergeordneten WifiAwareSession
zugeordnet. Wenn die übergeordnete Sitzung geschlossen wird, werden auch die zugehörigen Erkennungssitzungen geschlossen. Auch wenn verworfene Objekte geschlossen werden, kann das System nicht garantieren, dass Sitzungen, die nicht im Projektumfang enthalten sind, geschlossen werden. Daher empfehlen wir, die Methoden close()
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, der abonniert werden soll, und andere Konfigurationsattribute wie Übereinstimmungsfilter an. 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 Subscribe-Vorgang erfolgreich ist, ruft das System den onSubscribeStarted()
-Callback in der App auf. Da du das SubscribeDiscoverySession
-Argument im Callback verwenden kannst, um mit einem Publisher zu kommunizieren, nachdem deine App einen Publisher erkannt hat, solltest du diesen Verweis speichern. Du kannst die Abositzung jederzeit aktualisieren, indem du in der Erkennungssitzung updateSubscribe()
aufrufst.
An diesem Punkt wartet Ihr Abo, bis passende Verlage und Webpublisher im WLAN-Bereich 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 zu erstellen.
Wenn Sie einen Dienst nicht mehr abonnieren möchten, rufen Sie DiscoverySession.close()
auf.
Discovery-Sitzungen sind der übergeordneten WifiAwareSession
zugeordnet. Wenn die übergeordnete Sitzung geschlossen wird, werden auch die zugehörigen Erkennungssitzungen geschlossen. Auch wenn verworfene Objekte geschlossen werden, kann das System nicht garantieren, dass Sitzungen, die nicht im Projektumfang enthalten sind, geschlossen werden. Daher empfehlen wir, die Methoden close()
explizit aufzurufen.
Eine Nachricht posten
Zum Senden einer Nachricht an ein anderes Gerät benötigen Sie die folgenden Objekte:
Ein
DiscoverySession
. Mit diesem Objekt können SiesendMessage()
aufrufen. Ihre Anwendung erhält einDiscoverySession
, indem sie entweder einen Dienst veröffentlicht oder einen Dienst abonniert.PeerHandle
des anderen Geräts zum Weiterleiten der Nachricht. Deine App erhält denPeerHandle
eines anderen Geräts auf zwei Arten:- Ihre App veröffentlicht einen Dienst und erhält eine Nachricht von einem Abonnenten.
Deine App ruft die
PeerHandle
des Abonnenten aus demonMessageReceived()
-Callback ab. - Ihre App abonniert einen Dienst. Wenn ein übereinstimmender Publisher erkannt wird, ruft deine App die
PeerHandle
des Publishers aus demonServiceDiscovered()
-Callback ab.
- Ihre App veröffentlicht einen Dienst und erhält eine Nachricht von einem Abonnenten.
Deine App ruft die
Um eine Nachricht zu senden, rufen Sie sendMessage()
auf. Die folgenden Callbacks können dann auftreten:
- Wenn die Nachricht vom Peer erfolgreich empfangen wurde, ruft das System den
onMessageSendSucceeded()
-Callback in der Senden-App auf. - Wenn der Peer eine Nachricht empfängt, ruft das System den
onMessageReceived()
-Callback in der Empfänger-App auf.
Obwohl der PeerHandle
für die Kommunikation mit Peers erforderlich ist, sollten Sie sich nicht darauf als dauerhafte Kennung von Peers verlassen. Übergeordnete Kennungen 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 Erkennungsdienst einbetten. Die Methode setMatchFilter()
wirkt sich auf die Erkennung aus, die Methode setServiceSpecificInfo()
hingegen nicht.
Das Einbetten einer Kennung in eine Nachricht impliziert, dass das Nachrichtenbyte-Array so geändert wird, dass es eine Kennung enthält (z. B. die ersten 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:
Nutzen Sie die Wi-Fi Aware-Erkennung, um einen Dienst auf dem Server zu veröffentlichen und einen Dienst zu abonnieren (auf dem Client).
Sobald der Abonnent den Verlag oder Webpublisher gefunden hat, sende eine Nachricht vom Abonnenten an den Verlag oder Webpublisher.
Starte ein
ServerSocket
auf dem Gerät des Verlags oder Webpublishers und rufe entweder den Port fest oder rufe ihn ab:Kotlin
val ss = ServerSocket(0) val port = ss.localPort
Java
ServerSocket ss = new ServerSocket(0); int port = ss.getLocalPort();
Verwende den
ConnectivityManager
, um mit einemWifiAwareNetworkSpecifier
ein WLAN-sensitives Netzwerk beim Verlag oder Webpublisher anzufordern. Gib dabei die Erkennungssitzung und denPeerHandle
des Abonnenten an, den du aus der vom Abonnenten übertragenen Nachricht erhalten hast: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);
Wenn der Verlag oder Webpublisher ein Netzwerk anfordert, sollte er eine Nachricht an den Abonnenten senden.
Sobald der Abonnent die Nachricht vom Verlag oder Webpublisher erhalten hat, fordern Sie beim Abonnenten mit derselben Methode wie beim Verlag oder Webpublisher ein Wi-Fi Aware-Netzwerk an. Geben Sie beim Erstellen von
NetworkSpecifier
keinen Port an. Die entsprechenden Callback-Methoden werden aufgerufen, wenn die Netzwerkverbindung verfügbar ist, geändert wird oder unterbrochen wird.Sobald die Methode
onAvailable()
auf dem Abonnenten aufgerufen wird, steht einNetwork
-Objekt zur Verfügung, mit dem Sie einSocket
öffnen können, um mit demServerSocket
auf dem Publisher zu kommunizieren. Sie müssen jedoch die IPv6-Adresse und den Port vonServerSocket
kennen. Sie erhalten diese vomNetworkCapabilities
-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 die Netzwerkverbindung fertig ist, rufen Sie
unregisterNetworkCallback()
auf.
Verschiedene Mitbewerber und standortbezogene Informationen
Ein Gerät mit WLAN-RTT-Standort kann die Entfernung zu Peers direkt messen und diese Informationen verwenden, um die Erkennung des Wi-Fi Aware-Dienstes einzuschränken.
Die Wi-Fi RTT API ermöglicht das direkte Routing zu einem Wi-Fi Aware Peer entweder über seine MAC-Adresse oder sein PeerHandle.
Die Wi-Fi Aware-Erkennung kann so eingeschränkt werden, dass Dienste nur 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) ist.
Um Geofencing zu aktivieren, müssen sowohl der Publisher als auch der Abonnent Folgendes tun:
Der Publisher muss den Bereich mit setRangingEnabled(true) für den veröffentlichten Dienst aktivieren.
Wenn der Verlag oder Webpublisher die Entfernung nicht aktiviert, werden alle vom Abonnenten angegebenen Geofence-Einschränkungen ignoriert und eine normale Erkennung ausgeführt, wobei die Entfernung ignoriert wird.
Der Abonnent muss einen Geofence mit einer Kombination aus setMinDistanceMm und setMaxDistanceMm angeben.
Bei beiden Werten stellt eine nicht angegebene Entfernung keine Begrenzung dar. Nur die Angabe der maximalen Entfernung gilt für eine Mindestentfernung von 0. Nur die Angabe der Mindestentfernung hat keine Begrenzung.
Wenn ein Peer-Dienst innerhalb eines Geofence erkannt wird, wird der Callback onServiceDiscoveredWithinRange ausgelöst, der die gemessene Entfernung zum Peer angibt. Die Direct Wi-Fi RTT API kann dann bei Bedarf aufgerufen werden, um später die Entfernung zu messen.