Wi-Fi Aware – Übersicht

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 Konnektivität zwischen ihnen besteht. Wi-Fi Aware wird auch als Neighbor Awareness Networking (NAN) bezeichnet.

Wi-Fi Aware-Netzwerke bilden Cluster mit benachbarten Geräten oder es wird ein neuer Cluster erstellt, wenn das Gerät das erste Gerät 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 kommunizieren über die Wi-Fi Aware APIs mit dem Wi-Fi Aware-Systemdienst, 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 entdecken:Die API verfügt über einen Mechanismus, mit dem andere Geräte in der Nähe gefunden werden können. Der Vorgang beginnt, wenn auf einem Gerät mindestens ein sichtbarer Dienst veröffentlicht wird. Wenn ein Gerät dann einen oder mehrere Dienste abonniert und in die WLAN-Reichweite des Verlags oder Webpublishers eintritt, erhält der Abonnent eine Benachrichtigung, dass ein übereinstimmender Publisher gefunden wurde. Nachdem der Abonnent einen Publisher gefunden hat, kann er 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 sich gegenseitig erkannt haben, können sie eine bidirektionale Wi-Fi Aware-Netzwerkverbindung ohne Zugangspunkt herstellen.

Wi-Fi-Aware-Netzwerkverbindungen unterstützen höhere Durchsatzraten über längere Entfernungen als Bluetooth-Verbindungen. Diese Arten von Verbindungen sind nützlich für Anwendungen, die große Datenmengen zwischen Nutzern teilen, z. B. Apps zum Teilen von Fotos.

Verbesserungen unter Android 13 (API-Level 33)

Auf Geräten mit Android 13 (API-Level 33) und höher, die den Instant-Kommunikationsmodus unterstützen, können Apps die Methoden PublishConfig.Builder.setInstantCommunicationModeEnabled() und SubscribeConfig.Builder.setInstantCommunicationModeEnabled() verwenden, um den Instant-Kommunikationsmodus für eine Discovery-Sitzung von Publishern oder Abonnenten zu aktivieren oder zu deaktivieren. Der Modus für die sofortige Kommunikation beschleunigt den Nachrichtenaustausch, die Diensterkennung und jegliche Datenpfade, die im Rahmen einer Publisher- oder Abonnentenerkennungssitzung eingerichtet werden. Mit der Methode isInstantCommunicationModeSupported() können Sie ermitteln, ob ein Gerät den Modus für die sofortige Kommunikation unterstützt.

Verbesserungen unter Android 12 (API-Level 31)

Android 12 (API-Level 31) bietet einige Verbesserungen für Wi‐Fi Aware:

  • 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 wird oder sich außerhalb des Bereichs befindet.
  • Die Einrichtung von Wi-Fi Aware-Datenpfaden wurde vereinfacht. Frühere Versionen verwendeten L2-Messaging, um die MAC-Adresse des Initiators anzugeben, was zu Latenz führte. Auf Geräten mit Android 12 und höher kann der Teilnehmer (Server) so konfiguriert werden, dass er jeden Peer akzeptiert, d. h., er muss die MAC-Adresse des Initators im Voraus nicht kennen. Dies beschleunigt die Übertragung des Datenpfads und ermöglicht mehrere Punkt-zu-Punkt-Verbindungen mit nur einer Netzwerkanfrage.
  • Apps mit Android 12 oder höher können die Methode WifiAwareManager.getAvailableAwareResources() verwenden, um die Anzahl der derzeit verfügbaren Datenpfade sowie Veröffentlichungs- und Abositzungen abzurufen. So kann die Anwendung feststellen, ob genügend Ressourcen verfügbar sind, um die gewünschte Funktionalität auszuführen.

Ersteinrichtung

Führen Sie die folgenden Schritte aus, um Ihre App für die Verwendung der Wi-Fi Aware-Erkennung und -Netzwerke einzurichten:

  1. 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" />
    
  2. Prüfen Sie, 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);
    
  3. Prüfen Sie, ob WLAN Aware derzeit verfügbar ist. Wi-Fi Aware ist möglicherweise auf dem Gerät vorhanden, ist 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 werden. Wenn Sie prüfen möchten, ob WLAN Aware derzeit verfügbar ist, rufen Sie isAvailable() an.

    Die Verfügbarkeit von Wi-Fi Aware kann sich jederzeit ändern. Deine Anwendung sollte eine BroadcastReceiver registrieren, um ACTION_WIFI_AWARE_STATE_CHANGED zu empfangen, das gesendet wird, wenn sich die Verfügbarkeit ändert. Wenn Ihre App den Broadcast-Intent empfängt, sollten alle vorhandenen Sitzungen verworfen werden, wobei davon ausgegangen wird, dass der Wi-Fi Aware-Dienst unterbrochen wurde. Prüfen Sie dann den aktuellen Verfügbarkeitsstatus und passen Sie das Verhalten entsprechend an. 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 du Wi-Fi Aware nutzen kannst, muss deine App eine WifiAwareSession erhalten. Dazu muss sie attach() aufrufen. Diese Methode führt Folgendes aus:

  • Aktiviert die Wi-Fi Aware-Hardware.
  • Stellt einen Wi-Fi Aware-Cluster her oder bildet ihn.
  • 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 wird, führt das System den Callback onAttached() aus. Dieser Callback stellt ein WifiAwareSession-Objekt bereit, das von deiner App für alle weiteren Sitzungsvorgänge verwendet werden sollte. Eine Anwendung kann die Sitzung verwenden, um einen Dienst zu veröffentlichen oder einen Dienst zu abonnieren.

Ihre Anwendung 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 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() auf. Sie verwendet die folgenden Parameter:

  • PublishConfig gibt den Namen des Dienstes und andere Konfigurationsattribute 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.

Wenn Geräte, auf denen übereinstimmende Abonnenten-Apps ausgeführt werden, nach der Veröffentlichung in den WLAN-Bereich des Publishing-Geräts wechseln, erkennen die Abonnenten den Dienst. Wenn ein Abonnent einen Verlag oder Webpublisher findet, erhält der Verlag oder Webpublisher keine Benachrichtigung. Wenn der Abonnent dem Verlag oder Webpublisher jedoch eine Nachricht sendet, erhält der Verlag oder Webpublisher 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.

Rufen Sie DiscoverySession.close() auf, um die Veröffentlichung des Dienstes zu beenden. Discovery-Sitzungen sind mit ihrer übergeordneten WifiAwareSession verknüpft. Wenn die übergeordnete Sitzung geschlossen wird, werden auch die zugehörigen Erkennungssitzungen geschlossen. Auch wenn auch verworfene Objekte geschlossen werden, kann das System nicht garantieren, dass Sitzungen außerhalb des Geltungsbereichs 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:

  • SubscribeConfig gibt den Namen des zu abonnierenden Dienstes und andere Konfigurationsattribute wie den Abgleichsfilter 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 Abovorgang erfolgreich ist, ruft das System den onSubscribeStarted()-Callback in deiner App auf. Da du das SubscribeDiscoverySession-Argument im Callback verwenden kannst, um mit einem Publisher zu kommunizieren, nachdem deine App einen solchen erkannt hat, solltest du diese Referenz speichern. Sie können die Abositzung jederzeit aktualisieren, indem Sie in der Erkennungssitzung updateSubscribe() aufrufen.

Zu diesem Zeitpunkt wird für dein Abo gewartet, bis die übereinstimmenden Verlage und Webpublisher in den 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 herzustellen.

Wenn Sie einen Dienst nicht mehr abonnieren möchten, rufen Sie DiscoverySession.close() auf. Discovery-Sitzungen sind mit ihrer übergeordneten WifiAwareSession verknüpft. Wenn die übergeordnete Sitzung geschlossen wird, werden auch die zugehörigen Erkennungssitzungen geschlossen. Auch wenn auch verworfene Objekte geschlossen werden, kann das System nicht garantieren, dass Sitzungen außerhalb des Geltungsbereichs geschlossen werden. Daher empfehlen wir, die close()-Methoden explizit aufzurufen.

Eine Nachricht posten

Wenn Sie eine Nachricht an ein anderes Gerät senden möchten, benötigen Sie folgende Objekte:

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 onMessageSendSucceeded()-Callback in der sendenden App auf.
  • Wenn der Peer eine Nachricht erhält, ruft das System den onMessageReceived()-Callback in der empfangenden App auf.

Obwohl PeerHandle für die Kommunikation mit Peers erforderlich ist, sollten Sie sich nicht darauf als dauerhafte Kennzeichnung von Peers verlassen. Übergeordnete Kennungen können von der Anwendung verwendet werden, die in den Erkennungsdienst selbst oder in nachfolgenden Nachrichten eingebettet ist. Mit der Methode setMatchFilter() oder setServiceSpecificInfo() von PublishConfig oder SubscribeConfig können Sie eine Kennung in den Erkennungsdienst einbetten. Die Methode setMatchFilter() wirkt sich auf die Erkennung aus, die Methode setServiceSpecificInfo() hingegen nicht.

Bei der Einbettung einer Kennung in eine Nachricht wird das Nachrichtenbyte-Array so geändert, dass es eine Kennung enthält (z. B. als erste Byte).

Verbindung erstellen

Wi-Fi Aware unterstützt Client-Server-Netzwerke zwischen zwei Wi-Fi Aware-Geräten.

So richten Sie die Client-Server-Verbindung ein:

  1. Verwenden Sie die Wi‐Fi Aware-Erkennung, um einen Dienst zu veröffentlichen (auf dem Server) und einen Dienst zu abonnieren (auf dem Client).

  2. Sobald der Abonnent den Verlag oder Webpublisher erkannt hat, sende eine Nachricht von ihm an den Verlag oder Webpublisher.

  3. Starten Sie eine ServerSocket auf dem Gerät des Verlags oder Webpublishers 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();
    
  4. Verwenden Sie den ConnectivityManager, um ein Wi-Fi Aware-Netzwerk beim Verlag oder Webpublisher mithilfe eines WifiAwareNetworkSpecifier anzufordern. Geben Sie dabei die Erkennungssitzung und den PeerHandle des Abonnenten an, den 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);
    
  5. Sobald der Publisher ein Netzwerk anfordert, sollte eine Nachricht an den Abonnenten gesendet werden.

  6. Sobald der Abonnent die Nachricht vom Verlag oder Webpublisher erhält, fordern Sie mit derselben Methode wie beim Verlag oder Webpublisher ein Wi-Fi-Aware-Netzwerk beim Abonnenten an. Geben Sie beim Erstellen der NetworkSpecifier keinen Port an. Die entsprechenden Callback-Methoden werden aufgerufen, wenn die Netzwerkverbindung verfügbar, geändert oder unterbrochen ist.

  7. Sobald die Methode onAvailable() auf dem Abonnenten aufgerufen wurde, ist ein Network-Objekt verfügbar, mit dem Sie ein Socket öffnen können, um mit dem ServerSocket auf dem Verlag oder Webpublisher zu kommunizieren. Sie müssen jedoch die IPv6-Adresse und den IPv6-Port von ServerSocket kennen. Sie erhalten diese aus dem NetworkCapabilities-Objekt, das im onCapabilitiesChanged()-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);
    
  8. Wenn Sie die Netzwerkverbindung hergestellt haben, rufen Sie unregisterNetworkCallback() auf.

Verschiedene Freunde und Standorterkennung

Ein Gerät mit WLAN-RTT-Standortfunktionen kann die Entfernung zu Peers direkt messen und anhand dieser Informationen die Erkennung von Wi-Fi Aware-Diensten einschränken.

Die Wi-Fi RTT API ermöglicht die direkte Standortermittlung zu einem Wi-Fi Aware-Peer entweder über dessen MAC-Adresse oder über seinen 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 es ermöglicht, ein Gerät zu erkennen, 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) liegt.

Um Geofencing zu aktivieren, müssen sowohl der Publisher als auch der Abonnent Folgendes tun:

  • Der Verlag oder Webpublisher muss die Bereichserkennung für den veröffentlichten Dienst mit setRangingEnabled(true) aktivieren.

    Wenn der Publisher kein Bereichserkennung aktiviert, werden alle vom Abonnenten angegebenen Geofence-Einschränkungen ignoriert und eine normale Erkennung ohne Berücksichtigung der Entfernung ausgeführt.

  • Der Abonnent muss einen Geofence mit einer Kombination aus setMinDistanceMm und setMaxDistanceMm angeben.

    Bei beiden Werten gibt es keine Begrenzung, wenn die Entfernung nicht angegeben ist. Nur die Angabe der maximalen Entfernung impliziert eine Mindestentfernung von 0. Nur die Angabe des Mindestabstands impliziert keine Obergrenze.

Wenn ein Peer-Dienst innerhalb eines Geofence erkannt wird, wird der Callback onServiceDiscoveredWithinRange ausgelöst, der die gemessene Entfernung zum Peer angibt. Die direkte Wi-Fi RTT API kann dann bei Bedarf aufgerufen werden, um die Entfernung später zu messen.