Omówienie Wi-Fi Aware

Funkcje Wi-Fi Aware umożliwiają urządzeniom z Androidem 8.0 (poziom interfejsu API 26) oraz i odkrywać innych i nawiązywać z nimi bezpośredni kontakt, bez żadnego połączenia między nimi. Wi-Fi Aware jest też znane jako Znajomość sąsiadów Networking (Sieć).

Sieć Wi-Fi Aware działa poprzez tworzenie klastrów z sąsiednimi urządzeniami lub tworzenie nowego klastra, jeśli urządzenie jest pierwsze w danym obszarze. Ten zachowanie grupowania dotyczy całego urządzenia i jest zarządzane przez Wi-Fi usługa systemowa Aware; aplikacje nie mają kontroli nad zachowaniem grupowania. Aplikacje korzystają z interfejsów API Wi-Fi Aware, aby komunikować się z usługą systemową Wi-Fi Aware, która zarządza sprzętem Wi-Fi Aware na urządzeniu.

Interfejsy API Wi-Fi Aware umożliwiają aplikacjom wykonywanie tych operacji:

  • Wykrywanie innych urządzeń: interfejs API ma mechanizm znajdowania innych urządzeń. urządzenia w pobliżu. Proces rozpoczyna się, gdy jedno urządzenie opublikuje nowe lub bardziej wykrywalne usługi. Gdy urządzenie zasubskrybuje co najmniej 1 usługę i wejdzie w zakres sieci Wi-Fi wydawcy, subskrybent otrzyma powiadomienie o wykrywaniu odpowiedniego wydawcy. Po gdy subskrybent odkrywa wydawcę, może wysłać lub nawiązać połączenie sieciowe z wykrytym urządzeniem. Urządzenia mogą być jednocześnie wydawcami i subskrybentami.

  • Tworzenie połączenia sieciowego: po wykryciu siebie nawzajem przez 2 urządzenia mogą one utworzyć dwukierunkowe połączenie sieciowe Wi-Fi Aware bez punktu dostępu.

Połączenia sieciowe Wi-Fi Aware obsługują wyższe przepływności na większych dystansach niż połączenia Bluetooth. Tego typu połączenia są przydatne w aplikacjach, które dzielą się dużą ilością danych ilości danych przesyłanych między użytkownikami, np. w aplikacjach do udostępniania zdjęć.

Ulepszenia w Androidzie 13 (poziom 33 interfejsu API)

Na urządzeniach z Androidem 13 (poziom interfejsu API 33) lub nowszym, które obsługują tryb komunikacji natychmiastowej, aplikacje mogą używać metod PublishConfig.Builder.setInstantCommunicationModeEnabled()SubscribeConfig.Builder.setInstantCommunicationModeEnabled(), aby włączyć lub wyłączyć tryb komunikacji natychmiastowej w sesji odkrywania dla wydawcy lub subskrybenta. Tryb komunikacji natychmiastowej przyspiesza wymianę wiadomości, wykrywanie usług i jakiekolwiek ścieżki danych skonfigurowane w ramach sesji odkrywania wydawcy lub subskrybenta. Aby określić, czy urządzenie obsługuje komunikację błyskawiczną użyj metody isInstantCommunicationModeSupported().

Ulepszenia Androida 12 (poziom interfejsu API 31)

Android 12 (poziom API 31) wprowadza kilka ulepszeń funkcji Wi-Fi Aware:

  • Na urządzeniach z Androidem 12 (poziom interfejsu API 31) lub nowszym możesz używać funkcji onServiceLost() wywołanie zwrotne, aby otrzymywać alerty, gdy aplikacja utraci wykrytą usługę z powodu usługa zatrzymuje się lub znajduje się poza zasięgiem.
  • Konfiguracja ścieżek danych Aware Aware została uproszczona. Wcześniejsze wersje używały wiadomości L2 do przekazywania adresu MAC inicjatora, co powodowało opóźnienia. Na urządzeniach z Androidem 12 lub nowszym (serwer) można skonfigurować tak, aby akceptował dowolny peer, co oznacza, że nie jest wymagany adres MAC inicjatora. Przyspiesza to konfigurowanie ścieżki danych i umożliwia tworzenie wielu połączeń punkt-punkt za pomocą tylko jednego żądania sieciowego.
  • Aplikacje działające na Androidzie 12 lub nowszym mogą używać WifiAwareManager.getAvailableAwareResources() aby poznać liczbę obecnie dostępnych ścieżek danych, opublikować sesje, i sesji subskrypcji. Może to pomóc aplikacji określić, czy wystarczającą ilość dostępnych zasobów, aby uruchomić pożądaną funkcję.

Konfiguracja początkowa

Aby skonfigurować aplikację do korzystania z usług wykrywania i tworzenia sieci Wi-Fi Aware, wykonaj te czynności:

  1. W pliku manifestu aplikacji poproś o te uprawnienia:

    <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. Sprawdź, czy urządzenie obsługuje Wi-Fi Aware, korzystając z interfejsu API PackageManager, jak pokazano poniżej:

    KotlinJava
    context.packageManager.hasSystemFeature(PackageManager.FEATURE_WIFI_AWARE)
    context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_AWARE);
  3. Sprawdź, czy Wi-Fi Aware jest obecnie dostępne. Włączono Wi-Fi Aware tego urządzenia, ale mogą być niedostępne, ponieważ użytkownik wyłączył Wi-Fi lub Lokalizacja. W zależności od możliwości sprzętowych i oprogramowania niektóre urządzenia mogą nie obsługiwać Wi-Fi Aware, jeśli są używane w trybie Wi-Fi Direct, SoftAP lub tetheringu. Aby sprawdzić, czy funkcja Wi-Fi Aware jest obecnie dostępna, zadzwoń pod numer isAvailable()

    Dostępność Wi-Fi Aware może się zmieniać w dowolnym momencie. Aplikacja powinna zarejestrować BroadcastReceiver, aby otrzymywać ACTION_WIFI_AWARE_STATE_CHANGED, które jest wysyłane, gdy zmienia się dostępność. Gdy aplikacja otrzyma intencji, powinien odrzucić wszystkie istniejące sesje (przy założeniu, usługa Wi-Fi Aware została zakłócona), a następnie sprawdź obecny stan dostępności i odpowiednio dostosować jego działanie. Na przykład:

    KotlinJava
    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)
    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);

Więcej informacji znajdziesz w artykule Transmisje.

Uzyskiwanie sesji

Aby zacząć korzystać z Wi-Fi Aware, aplikacja musi uzyskaćWifiAwareSession, wywołującattach(). Ta metoda:

  • Włącza sprzęt Wi-Fi Aware.
  • Dołącza do klastra Wi-Fi Aware lub tworzy taki klaster.
  • Tworzy sesję Wi-Fi Aware z unikalną przestrzenią nazw, która pełni funkcję dla wszystkich utworzonych w nim sesji wykrywania.

Jeśli aplikacja się przyłączy, system wykona polecenie onAttached() oddzwonienie. To wywołanie zwrotne udostępnia obiekt WifiAwareSession których aplikacja powinna używać we wszystkich kolejnych operacjach sesji. Aplikacja może użyć sesji do publikowania usługi lub subskrybowania usługi.

Aplikacja powinna wywołać funkcję attach() tylko raz. Jeśli aplikacja wywołuje attach() aplikacji wiele razy, dla każdego połączenia jest odbierana inna sesja, z której każda ma własną przestrzeń nazw. Może to być przydatne w złożonych scenariuszach, ale powinno których zasadniczo można uniknąć.

Publikowanie usługi

Aby ustawić usługę jako wykrywalną, wywołaj metodę publish(), która przyjmuje następujące parametry:

  • PublishConfig określa nazwę i innych właściwości konfiguracji, takich jak filtr dopasowania.
  • DiscoverySessionCallback określa działania, które mają być wykonywane w przypadku wystąpienia zdarzeń, np. gdy subskrybent odbiera wiadomość.

Oto przykład:

KotlinJava
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) {
        ...
    }
})
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);

Jeśli publikacja się powiedzie, wywołana zostanie metoda wywołania zwrotnego onPublishStarted().

Po opublikowaniu urządzenia z odpowiednimi aplikacjami dla subskrybentów będą przenoszone do w zasięgu Wi-Fi urządzenia wydawniczego, subskrybenci odkrywają usługę. Kiedy gdy subskrybent znajduje wydawcę, wydawca nie otrzymuje powiadomienia; jeśli subskrybent wyśle wiadomość do wydawcy, to wydawca otrzyma powiadomienie. Wtedy wywoływana jest metoda wywołania zwrotnego onMessageReceived(). Za pomocą PeerHandle argument z tej metody do Odeślij wiadomość do subskrybenta lub utworzyć z nim połączenie.

Aby zatrzymać publikowanie usługi, zadzwoń pod numer DiscoverySession.close(). Sesje wyszukiwania są powiązane z elementem nadrzędnym WifiAwareSession. Jeśli sesja nadrzędna to powiązane z nim sesje odkrywania również zostaną zamknięte. Porzucone obiekty są zamknięte, ale system nie gwarantuje, że sesje poza zakresem są zamknięte, dlatego zalecamy wywołanie metody close().

Subskrybowanie usługi

Aby zasubskrybować usługę, wywołaj metodę subscribe(), która przyjmuje te parametry:

  • SubscribeConfig określa nazwę usługi, którą chcesz subskrybować, oraz innych właściwości konfiguracji, takich jak dopasowanie filtr.
  • DiscoverySessionCallback określa działania, które mają być wykonywane w razie wystąpienia zdarzeń, np. po wykryciu wydawcy.

Oto przykład:

KotlinJava
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)
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);

Jeśli operacja subskrypcji się powiedzie, system wywoła w aplikacji funkcję onSubscribeStarted(). Ponieważ w funkcjach wywołania możesz używać argumentu SubscribeDiscoverySession do komunikacji z wydawcą po wykryciu przez aplikację wydawcy, zaoszczędź to odwołanie. Sesję subskrypcji możesz zaktualizować w dowolnym momencie, połączenia updateSubscribe() podczas sesji odkrywania.

Na tym etapie Twoja subskrypcja czeka na pojawienie się odpowiednich wydawców. Zasięg Wi-Fi. W takim przypadku system wykonuje metodę wywołania zwrotnego onServiceDiscovered(). Argument PeerHandle z tego wywołania zwrotnego umożliwia wysłanie wiadomości lub utworzenie połączenia z tym wydawcą.

Aby zrezygnować z subskrypcji usługi, zadzwoń pod numer DiscoverySession.close(). Sesje wyszukiwania są powiązane z elementem nadrzędnym WifiAwareSession. Jeśli sesja nadrzędna to powiązane z nim sesje odkrywania również zostaną zamknięte. Porzucone nawet zamknięte obiekty, system nie gwarantuje, że są poza zakresem są zamknięte, dlatego zalecamy wywołanie metody close() .

Wyślij wiadomość

Aby wysłać wiadomość na inne urządzenie, potrzebne będą te obiekty:

Aby wysłać wiadomość, zadzwoń pod numer sendMessage() W tym przypadku mogą wystąpić następujące wywołania zwrotne:

  • Po pomyślnym odebraniu komunikatu przez peera system wywołuje metodę onMessageSendSucceeded() wywołanie zwrotne w aplikacji wysyłanie.
  • Gdy połączenie równorzędne otrzyma wiadomość, system wywoła metodę onMessageReceived() wywołanie zwrotne w aplikacji receiving.
.

Chociaż PeerHandle jest wymagany do komunikacji z urządzeniami peer-to-peer, nie należy polegać na nim jako trwałym identyfikatorze urządzeń peer-to-peer. Identyfikatory wyższego poziomu mogą być używane przez aplikację – osadzone w samej usłudze wykrywania lub w kolejne wiadomości. Identyfikator możesz osadzić w usłudze wyszukiwania za pomocą metody setMatchFilter() lub setServiceSpecificInfo() PublishConfig lub SubscribeConfig. Metoda setMatchFilter() wpływa na odkrywanie, a metoda setServiceSpecificInfo() nie ma na to wpływu.

Wstawianie identyfikatora w wiadomości oznacza modyfikowanie tablicy bajtów wiadomości w celu uwzględnienia identyfikatora (np. jako pierwszych kilku bajtów).

Utwórz połączenie

Wi-Fi Aware obsługuje sieć klient-serwer między dwoma urządzeniami Wi-Fi Aware.

Aby skonfigurować połączenie klienta z serwerem:

  1. Użyj wykrywania Wi-Fi Aware, aby opublikować usługę (na serwerze) i zasubskrybować usługę (na kliencie).

  2. Gdy subskrybent odkryje wydawcę, Wyślij wiadomość od subskrybenta do wydawcy.

  3. Uruchom ServerSocket na urządzeniu wydawcy i ustaw lub uzyskaj jego port:

    KotlinJava
    val ss = ServerSocket(0)
    val port = ss.localPort
    ServerSocket ss = new ServerSocket(0);
    int port = ss.getLocalPort();
  4. Skorzystaj z narzędzia ConnectivityManager, aby: żądania sieci Wi-Fi Aware u wydawcy za pomocą WifiAwareNetworkSpecifier, określając sesję wykrywania i PeerHandle subskrybenta, uzyskane z wiadomości przesłanej przez subskrybenta:

    KotlinJava
    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);
    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. Gdy wydawca poprosi o dostęp do sieci, Wyślij wiadomość do subskrybenta.

  6. Gdy subskrybent otrzyma wiadomość od wydawcy, poproś o utworzenie sieci Wi-Fi Aware u subskrybenta, używając tej samej metody co w przypadku wydawcy. Tak nie określaj portu podczas tworzenia NetworkSpecifier Odpowiednie metody wywołania są wywoływane, gdy połączenie z siecią jest dostępne, zmienione lub utracone.

  7. Po wywołaniu metody onAvailable() przez subskrybenta Obiekt Network jest dostępny w: otwórz Socket, aby się z nim skontaktować z ServerSocket na poziomie wydawcy. Adres IPv6 i port serwera ServerSocket. Dane te pochodzą z NetworkCapabilities obiekt podane w wywołaniu zwrotnym onCapabilitiesChanged():

    KotlinJava
    val peerAwareInfo = networkCapabilities.transportInfo as WifiAwareNetworkInfo
    val peerIpv6 = peerAwareInfo.peerIpv6Addr
    val peerPort = peerAwareInfo.port
    ...
    val socket = network.getSocketFactory().createSocket(peerIpv6, peerPort)
    WifiAwareNetworkInfo peerAwareInfo = (WifiAwareNetworkInfo) networkCapabilities.getTransportInfo();
    Inet6Address peerIpv6 = peerAwareInfo.getPeerIpv6Addr();
    int peerPort = peerAwareInfo.getPort();
    ...
    Socket socket = network.getSocketFactory().createSocket(peerIpv6, peerPort);
  8. Po zakończeniu konfigurowania połączenia z siecią zadzwoń na numer unregisterNetworkCallback().

    .

Różne grupy rówieśników i odkrywanie z uwzględnieniem lokalizacji

Urządzenie z lokalizacją RTT przez Wi-Fi. mogą bezpośrednio mierzyć odległość od rówieśników i używać tych informacji do ograniczyć wykrywanie usług Wi-Fi Aware.

Interfejs Wi-Fi RTT API umożliwia bezpośrednie docieranie do równorzędnej sieci Wi-Fi Aware za pomocą Adres MAC lub jego PeerHandle.

Wykrywanie Wi-Fi Aware może być ograniczone do wykrywania tylko usług w określonym geoogrodzeniu. Możesz na przykład skonfigurować geofence, który umożliwia wykrywanie urządzenia publikującego usługę "Aware_File_Share_Service_Name" w odległości nie mniejszej niż 3 metry (określanej jako 3000 mm) i nie większej niż 10 metrów (określanej jako 10 000 mm).

Aby włączyć geofencing, zarówno wydawca, jak i subskrybent muszą wykonać działanie:

  • Wydawca musi włączyć zakres w opublikowanej usłudze za pomocą funkcji setRangingEnabled(true).

    Jeśli wydawca nie włączy zakresu, wszelkie ograniczenia geofencingu określone przez subskrybenta są ignorowane, a wykrywanie odbywa się normalnie. ignorując odległość.

  • Subskrybent musi określić geofence za pomocą kombinacji setMinOdległośćMm oraz setMaxOdległośćMm.

    W przypadku każdej z tych wartości nieokreślony dystans oznacza brak limitu. Tylko określanie maksymalna odległość oznacza minimalną odległość równą 0. Parametr minimalna odległość nie oznacza maksymalnej odległości.

W przypadku wykrycia w obszarze geofencingu usługi równorzędnej onServiceDiscoveredWithinRange (w języku angielskim) wywołanie zwrotne, które podaje zmierzoną odległość do elementu równorzędnego. Interfejs Direct Wi-Fi RTT API można następnie w razie potrzeby wywoływać w celu pomiaru odległości później.