Wi-Fi Aware – Übersicht

Wi-Fi Aware-Funktionen sind für Geräte mit Android 8.0 (API-Level 26) und und eine direkte Verbindung zueinander herstellen, ohne dass Konnektivität zwischen ihnen. Wi‑Fi Aware wird auch als Neighbor Awareness Networking (NAN) bezeichnet.

Bei einem Wi-Fi Aware-Netzwerk werden Cluster mit benachbarten Geräten gebildet. indem ein neuer Cluster erstellt wird, wenn das Gerät das erste Gerät in einem Bereich ist. Dieses Cluster-Verhalten gilt für das gesamte Gerät und wird vom Wi‑Fi Aware-Systemdienst verwaltet. Apps haben keine Kontrolle über das Cluster-Verhalten. Apps nutzen über die Wi-Fi Aware APIs mit dem Wi-Fi Aware-Systemdienst kommunizieren, der die Wi-Fi Aware-Hardware des Geräts.

Mit den Wi-Fi Aware APIs können Apps die folgenden Vorgänge ausführen:

  • Andere Geräte entdecken:Die API hat einen Mechanismus, mit dem Geräte in der Nähe. Der Prozess beginnt, wenn ein Gerät ein Gerät veröffentlicht oder besser auffindbaren Diensten. Wenn ein Gerät ein oder mehrere Abos abonniert und in den WLAN-Bereich des Verlags oder Webpublishers gelangt, erhält der Abonnent dass ein übereinstimmender Publisher gefunden wurde. Nach dem einen Verlag oder Webpublisher entdeckt, kann er entweder ein kurzes oder eine Netzwerkverbindung mit dem erkannten Gerät herstellen. Geräte können gleichzeitig Publisher und Abonnenten sein.

  • Netzwerkverbindung herstellen: Nachdem zwei Geräte sich gefunden haben, können sie eine bidirektionale Wi‑Fi Aware-Netzwerkverbindung ohne Zugangspunkt herstellen.

Wi-Fi Aware-Netzwerkverbindungen unterstützen höhere Durchsatzraten bei längeren Entfernungen als bei Bluetooth Verbindungen. Diese Arten von Verbindungen eignen sich für Apps, bei denen große Datenmengen zwischen Nutzern geteilt werden, z. B. Foto-Teilen-Apps.

Verbesserungen für 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 Discovery-Sitzung von Publishern oder Abonnenten zu aktivieren oder zu deaktivieren. Der Modus für die sofortige Kommunikation beschleunigt den Nachrichtenaustausch, die Diensterkennung und alle Datenpfade, die im Rahmen einer Discovery-Sitzung für Publisher oder Abonnenten eingerichtet wurden. Mit der Methode isInstantCommunicationModeSupported() kannst du feststellen, 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 die onServiceLost() um benachrichtigt zu werden, wenn in deiner App ein erkannter Dienst aufgrund des wenn der Dienst anhält oder sich außerhalb des zulässigen Bereichs befindet.
  • Die Einrichtung von Wi‑Fi Aware-Datenpaden wurde vereinfacht. Frühere Versionen L2-Messaging verwendet, um die MAC-Adresse des Initiators bereitzustellen, die verursachte Latenz. Auf Geräten mit Android 12 und höher (Server) kann so konfiguriert werden, dass er jeden Peer akzeptiert. um die MAC-Adresse des Initators zu erfahren. Dadurch wird der Datenpfad mehrere Point-to-Point-Links mit nur einem Netzwerk ermöglichen.
  • Apps mit Android 12 oder höher können die WifiAwareManager.getAvailableAwareResources() die Anzahl der aktuell verfügbaren Datenpfade, Veröffentlichungssitzungen, und abonnieren Sitzungen. So kann die App feststellen, ob genügend Ressourcen verfügbar sind, um die gewünschten Funktionen auszuführen.

Ersteinrichtung

So richten Sie Ihre App für die Suche und Vernetzung mit Wi‑Fi Aware ein:

  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. Überprüfe, ob das Gerät Wi-Fi Aware unterstützt mit der PackageManager API, wie unten gezeigt:

    Kotlin

    context.packageManager.hasSystemFeature(PackageManager.FEATURE_WIFI_AWARE)

    Java

    context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_AWARE);
  3. 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 Standort deaktiviert hat. Je nach Hardware- und Firmwarefunktionen unterstützen einige Geräte möglicherweise keine WLAN-Aware-Funktion, wenn Wi‑Fi Direct, SoftAP oder Tethering verwendet wird. Wenn Sie prüfen möchten, ob Wi‑Fi Aware derzeit verfügbar ist, geben Sie isAvailable() ein.

    Die Verfügbarkeit von Wi-Fi Aware kann sich jederzeit ändern. Ihre App sollte BroadcastReceiver registrieren, um ACTION_WIFI_AWARE_STATE_CHANGED, die gesendet wird, wenn sich die Verfügbarkeit ändert. Wenn deine App die Broadcast-Intent alle vorhandenen Sitzungen verwerfen der Wi-Fi Aware-Dienst unterbrochen wurde, und überprüfe die den aktuellen Verfügbarkeitsstatus und passen 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 Übertragungen.

Sitzung abrufen

Um Wi-Fi Aware nutzen zu können, benötigt deine App ein WifiAwareSession über folgenden Anruf attach(). Diese Methode führt Folgendes aus:

  • Aktiviert die Wi-Fi Aware-Hardware.
  • Tritt einem Wi‑Fi Aware-Cluster bei oder bildet einen solchen.
  • Erstellt eine Wi-Fi Aware-Sitzung mit einem eindeutigen Namespace, der als Container für alle darin erstellten Erkennungssitzungen.

Wenn die Anwendung erfolgreich angehängt wurde, führt das System die onAttached()-Callback. Dieser Rückruf 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.

Deine 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

Um einen Dienst auffindbar zu machen, rufen Sie die Methode publish()-Methode, die Für sind folgende Parameter erforderlich:

  • PublishConfig gibt den Namen des Dienstes und andere Konfigurationseigenschaften wie den Abgleichsfilter an.
  • Mit DiscoverySessionCallback werden die Aktionen festgelegt, die ausgeführt werden sollen, wenn Ereignisse auftreten, 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, ist der onPublishStarted() Callback-Methode aufgerufen.

Nach der Veröffentlichung können Abonnenten den Dienst auf Geräten mit entsprechenden Abo-Apps nutzen, die sich im WLAN-Bereich des veröffentlichenden Geräts befinden. Wenn ein Abonnent einen Publisher findet, erhält dieser keine Benachrichtigung. Wenn der Abonnent jedoch eine Nachricht an den Publisher sendet, erhält dieser eine Benachrichtigung. In diesem Fall onMessageReceived() Callback-Methode aufgerufen. Mit dem Argument PeerHandle dieser Methode kannst du eine Nachricht an den Abonnenten zurücksenden oder eine Verbindung zu ihm herstellen.

Um die Veröffentlichung des Dienstes zu beenden, rufen Sie DiscoverySession.close() Discovery-Sitzungen sind mit dem übergeordneten Element verknüpft WifiAwareSession Wenn die übergeordnete Sitzung geschlossen wurden, werden die zugehörigen Discovery-Sitzungen ebenfalls geschlossen. Auch verworfene Objekte werden geschlossen. Das System kann jedoch nicht garantieren, dass nicht mehr gültige Sitzungen geschlossen werden. Wir empfehlen daher, die close()-Methoden explizit aufzurufen.

Einen Dienst abonnieren

Rufen Sie zum Abonnieren eines Dienstes die Methode subscribe(), Hierfür werden folgende Parameter verwendet:

  • SubscribeConfig gibt den Namen des zu abonnierenden Dienstes und andere Konfigurationseigenschaften wie den Abgleichsfilter an.
  • DiscoverySessionCallback gibt die Aktionen, die ausgeführt werden sollen, wenn Ereignisse eintreten, z. B. wenn ein Publisher entdeckt 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 war, ruft das System den onSubscribeStarted()-Callback in deiner App auf. Da du mit dem Argument SubscribeDiscoverySession im Callback mit einem Publisher kommunizieren kannst, nachdem deine App einen gefunden hat, solltest du diese Referenz speichern. Sie können die Abonnieren-Sitzung jederzeit aktualisieren, indem Sie Anrufen updateSubscribe() in der Discovery-Sitzung.

In diesem Fall wartet dein Abo darauf, dass übereinstimmende Publisher in Reichweite des WLANs kommen. In diesem Fall führt das System onServiceDiscovered() . Du kannst das Argument PeerHandle aus diesem Rückruf verwenden, um eine Nachricht an diesen Publisher zu senden oder eine Verbindung zu ihm herzustellen.

Wenn Sie einen Dienst nicht mehr abonnieren möchten, rufen Sie DiscoverySession.close() Discovery-Sitzungen sind mit dem übergeordneten Element verknüpft WifiAwareSession Wenn die übergeordnete Sitzung geschlossen wurden, werden die zugehörigen Discovery-Sitzungen ebenfalls geschlossen. Auch verworfene Objekte werden geschlossen. Das System kann jedoch nicht garantieren, dass nicht mehr gültige Sitzungen geschlossen werden. Wir empfehlen daher, 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:

Wenn Sie eine Nachricht senden möchten, wählen Sie sendMessage(). Die folgenden Callbacks können dann auftreten:

  • Wenn die Nachricht erfolgreich vom Peer empfangen wurde, ruft das System die onMessageSendSucceeded() in der Senden-App zurückgegeben.
  • Wenn der Peer eine Nachricht empfängt, ruft das System den onMessageReceived()-Callback in der empfangenden App auf.

Obwohl die PeerHandle für die Kommunikation mit Gleichgesinnten erforderlich ist, sollten Sie Folgendes nicht tun: als permanente Peer-ID. Übergeordnete Kennungen können die von der Anwendung verwendet werden und 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 Die setMatchFilter()-Methode wirkt sich auf die Erkennung aus, während die Methode Die Methode setServiceSpecificInfo() wirkt sich nicht auf die Erkennung aus.

Das Einbetten einer Kennung in eine Nachricht impliziert das Ändern des Nachrichtenbyte-Arrays auf eine Kennung enthalten (z. B. als erstes 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:

  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 Verleger entdeckt hat, Sendet eine Nachricht vom Abonnenten an den Publisher.

  3. ServerSocket auf dem Publisher starten Gerät und legen Sie den Port fest oder rufen Sie diesen ab:

    Kotlin

    val ss = ServerSocket(0)
    val port = ss.localPort

    Java

    ServerSocket ss = new ServerSocket(0);
    int port = ss.getLocalPort();
  4. Mit dem ConnectivityManager können Sie Wi-Fi Aware-Netzwerk beim Verlag oder Webpublisher über eine WifiAwareNetworkSpecifier, unter Angabe der Erkennungssitzung und der PeerHandle des Abonnenten, 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);
  5. Sobald der Publisher ein Netzwerk anfordert, sollte er dem Abonnenten eine Nachricht senden.

  6. Fordere ein WLAN an, sobald der Abonnent die Nachricht vom Verlag oder Webpublisher erhalten hat Aware-Netzwerk beim Abonnenten mit derselben Methode wie beim Verlag oder Webpublisher. Geben Sie beim Erstellen der NetworkSpecifier keinen Port an. Die werden die entsprechenden Callback-Methoden aufgerufen, wenn die Netzwerkverbindung verfügbar, geändert oder verloren gehen.

  7. Wenn die Methode onAvailable() für den Abonnenten aufgerufen wird, Objekt Network ist verfügbar mit Sie können ein Socket öffnen, mit dem ServerSocket auf dem Herausgeber. Sie müssen jedoch wissen, IPv6-Adresse und -Port von ServerSocket Sie erhalten diese vom 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 mit der Netzwerkverbindung fertig sind, rufen Sie unregisterNetworkCallback() auf.

Verschiedene Freunde und Standorterkennung

Ein Gerät mit Wi‑Fi RTT-Standortfunktionen kann die Entfernung zu Peers direkt messen und diese Informationen verwenden, um die Wi‑Fi Aware-Diensterkennung einzuschränken.

Die Wi‑Fi RTT API ermöglicht die direkte Entfernungsmessung zu einem Wi‑Fi Aware-Peer entweder über seine MAC-Adresse oder seinen PeerHandle.

Die Wi-Fi Aware-Erkennung kann eingeschränkt werden, sodass nur Dienste in einem bestimmten Geofence. Sie können z. B. einen Geofence einrichten, eines Geräts, 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).

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

  • Der Publisher muss die Abfrage für den veröffentlichten Dienst mit setRangingEnabled(true) aktivieren.

    Wenn der Verlag oder Webpublisher die Standortermittlung nicht aktiviert, werden alle vom Abonnenten angegebenen Geofence-Einschränkungen ignoriert und die normale Erkennung wird durchgeführt, wobei die Entfernung ignoriert wird.

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

    Bei beiden Werten gibt es keine Begrenzung, wenn die Entfernung nicht angegeben ist. Nur Angabe der maximale Abstand impliziert eine minimale Entfernung von 0. Nur die Angabe des Mindestabstand impliziert kein Maximum.

Wenn ein Peer-Dienst innerhalb eines Geofences erkannt wird, wird der Rückruf onServiceDiscoveredWithinRange ausgelöst, der die gemessene Entfernung zum Peer liefert. Die die direkte Wi-Fi RTT API aufrufen, um die Entfernung später ansehen.