Funkcje Wi-Fi Aware umożliwiają urządzeniom z Androidem 8.0 (poziom interfejsu API 26) i nowszym wykrywaniem i łączeniem się bezpośrednio ze sobą bez jakiegokolwiek innego połączenia. Wi-Fi Aware jest też nazywane siecią świadomości sąsiadów (NAN).
Obsługa sieci Wi-Fi Aware polega na tworzeniu klastrów z urządzeniami sąsiednimi lub przez utworzenie nowego klastra, jeśli urządzenie jest pierwsze w danym obszarze. Takie działanie grupowania dotyczy całego urządzenia i jest zarządzane przez usługę systemową Wi-Fi Aware. Aplikacje nie mają kontroli nad działaniem grupowania. Aplikacje korzystają z interfejsów Wi-Fi Aware API, by komunikować się z usługą systemową Wi-Fi Aware, która zarządza sprzętem Wi-Fi Aware na urządzeniu.
Interfejsy Wi-Fi Aware API pozwalają aplikacjom wykonywać te operacje:
Wykrywanie innych urządzeń: interfejs API ma mechanizm znajdowania innych urządzeń w pobliżu. Ten proces rozpoczyna się, gdy jedno urządzenie opublikuje co najmniej 1 usługę wykrywalną. Następnie, gdy urządzenie subskrybuje co najmniej jedną usługę i znajduje się w zasięgu Wi-Fi wydawcy, subskrybent otrzyma powiadomienie o wykryciu pasującego wydawcy. Gdy subskrybent odkryje wydawcę, może wysłać krótką wiadomość lub nawiązać połączenie sieciowe z wykrytym urządzeniem. Urządzenia mogą być jednocześnie jednocześnie wydawcami i subskrybentami.
Utwórz połączenie sieciowe: gdy 2 urządzenia odnajdą się, mogą nawiązać dwukierunkowe połączenie sieciowe Wi-Fi Aware bez punktu dostępu.
Połączenia sieciowe Wi-Fi Aware obsługują większą przepustowość na dłuższych dystansach niż połączenia Bluetooth. Tego typu połączenia są przydatne w aplikacjach, które udostępniają duże ilości danych między użytkownikami, np. w aplikacjach do udostępniania zdjęć.
Ulepszenia Androida 12 (poziom interfejsu API 31)
Android 12 (poziom interfejsu API 31) wprowadza kilka ulepszeń do Wi-Fi Aware:
- Na urządzeniach z Androidem 12 (poziom interfejsu API 31) lub nowszym możesz użyć wywołania zwrotnego
onServiceLost()
, aby otrzymywać alerty, gdy aplikacja utraci wykrytą usługę z powodu zatrzymania lub wykroczenia poza zasięg usługi. - Konfiguracja ścieżek danych Wi-Fi Aware została uproszczona. We wcześniejszych wersjach do dostarczenia adresu MAC inicjatora używano komunikatów L2, co spowodowało opóźnienie. Na urządzeniach z Androidem 12 lub nowszym można skonfigurować odpowiedź (serwer) tak, aby akceptowała wszystkie połączenia równorzędne, co oznacza, że nie trzeba od razu znać adresu MAC inicjatora. Przyspiesza to tworzenie ścieżki danych i umożliwia wiele połączeń między punktami za pomocą tylko jednego żądania sieciowego.
- Aplikacje działające na Androidzie 12 lub nowszym mogą używać metody
WifiAwareManager.getAvailableAwareResources()
, aby sprawdzać liczbę obecnie dostępnych ścieżek danych oraz publikować i subskrybować sesje. Pomaga to aplikacji określić, czy ilość dostępnych zasobów jest wystarczająca do wykonania wymaganej funkcji.
Konfiguracja początkowa
Aby skonfigurować w aplikacji wykrywanie sieci i wykrywanie Wi-Fi Aware, wykonaj te czynności:
Poproś o te uprawnienia w pliku manifestu aplikacji:
<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" />
Sprawdź, czy urządzenie obsługuje funkcję Wi-Fi Aware z interfejsem API
PackageManager
, jak pokazano poniżej:Kotlin
context.packageManager.hasSystemFeature(PackageManager.FEATURE_WIFI_AWARE)
Java
context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_AWARE);
Sprawdź, czy sieć Wi-Fi Aware jest obecnie dostępna. Wi-Fi Aware może istnieć na urządzeniu, ale obecnie może nie być dostępne, ponieważ użytkownik wyłączył Wi-Fi lub lokalizację. W zależności od możliwości sprzętu i oprogramowania układowego niektóre urządzenia mogą nie obsługiwać Wi-Fi Aware, jeśli korzystasz z Wi-Fi Direct, SoftAP lub tetheringu. Aby sprawdzić, czy Wi-Fi Aware jest obecnie dostępne, zadzwoń pod numer
isAvailable()
.Dostępność Wi-Fi Aware może się zmienić w każdej chwili. Twoja aplikacja powinna zarejestrować element
BroadcastReceiver
, aby otrzymaćACTION_WIFI_AWARE_STATE_CHANGED
, który jest wysyłany zawsze, gdy zmieni się dostępność. Gdy aplikacja otrzyma intencję transmisji, powinna odrzucić wszystkie istniejące sesje (przy założeniu, że usługa Wi-Fi Aware została zakłócona), a następnie sprawdzić bieżący stan dostępności i odpowiednio dostosować swoje działanie. Na przykład: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);
Więcej informacji znajdziesz w sekcji Komunikaty.
Pobieranie sesji
Aby zacząć korzystać z Wi-Fi Aware, aplikacja musi uzyskać WifiAwareSession
, dzwoniąc pod numer attach()
. Ta metoda:
- Włącza sprzęt Wi-Fi Aware.
- Dołącza lub tworzy klaster Wi-Fi Aware.
- Tworzy sesję Wi-Fi Aware z unikalną przestrzenią nazw, która działa jako kontener na wszystkie utworzone w niej sesje wykrywania.
Jeśli aplikacja się połączy, system wykona wywołanie zwrotne onAttached()
.
To wywołanie zwrotne udostępnia obiekt WifiAwareSession
, którego aplikacja powinna używać przy wszystkich kolejnych operacjach w ramach sesji. Aplikacja może wykorzystać sesję do opublikowania usługi lub subskrypcji usługi.
Aplikacja powinna wywołać metodę attach()
tylko raz. Jeśli aplikacja wielokrotnie wywołuje funkcję attach()
, dla każdego wywołania otrzymuje inną sesję z własną przestrzenią nazw. Może to być przydatne w złożonych scenariuszach, ale zasadniczo należy tego unikać.
Publikowanie usługi
Aby usługa stała się wykrywalna, wywołaj metodę publish()
, która przyjmuje te parametry:
PublishConfig
określa nazwę usługi i inne właściwości konfiguracji, np. filtr dopasowania.DiscoverySessionCallback
określa działania, które mają być wykonywane, gdy wystąpią zdarzenia, na przykład gdy subskrybent otrzyma wiadomość.
Oto przykład:
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);
Jeśli publikacja się powiedzie, wywoływana jest metoda wywołania zwrotnego onPublishStarted()
.
Gdy po publikacji urządzenia z pasującymi aplikacjami do subskrypcji przejdą do zasięgu Wi-Fi urządzenia publikującego, subskrybenci odkrywają usługę. Gdy subskrybent znajdzie wydawcę, wydawca nie otrzyma powiadomienia. Jeśli jednak subskrybent wyśle do niego wiadomość, otrzyma on powiadomienie. W takim przypadku wywoływana jest metoda wywołania zwrotnego onMessageReceived()
. Możesz użyć argumentu PeerHandle
z tej metody, aby wysłać wiadomość z powrotem do subskrybenta lub utworzyć z nią połączenie.
Aby zakończyć publikowanie usługi, wywołaj DiscoverySession.close()
.
Sesje Discovery są powiązane z kontem nadrzędnym WifiAwareSession
. Jeśli sesja nadrzędna zostanie zamknięta, powiązane z nią sesje wykrywania również zostaną zamknięte. Odrzucone obiekty również są zamykane, ale system nie gwarantuje, że sesje spoza zakresu zostaną zamknięte. Dlatego zalecamy jawne wywoływanie metod 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 zasubskrybować, i inne właściwości konfiguracji, takie jak filtr dopasowania. DiscoverySessionCallback
określa działania, które mają być wykonywane w momencie wystąpienia zdarzeń, np. po wykryciu wydawcy.
Oto przykład:
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);
Jeśli operacja subskrypcji się powiedzie, system wywoła w aplikacji wywołanie zwrotne onSubscribeStarted()
. Za pomocą argumentu SubscribeDiscoverySession
w wywołaniu zwrotnym możesz komunikować się z wydawcą, gdy aplikacja go wykryje, dlatego warto zapisać to odwołanie. W każdej chwili możesz zaktualizować sesję subskrypcji, wywołując updateSubscribe()
w sesji wykrywania.
W tym momencie subskrypcja czeka, aż pasujących wydawców znajdzie się w zasięgu sieci Wi-Fi. W takim przypadku system wykonuje metodę wywołania zwrotnego onServiceDiscovered()
. Aby wysłać wiadomość lub utworzyć połączenie z tym wydawcą, możesz użyć argumentu PeerHandle
.
Aby przestać subskrybować usługę, wywołaj DiscoverySession.close()
.
Sesje Discovery są powiązane z kontem nadrzędnym WifiAwareSession
. Jeśli sesja nadrzędna zostanie zamknięta, powiązane z nią sesje wykrywania również zostaną zamknięte. Odrzucone obiekty również są zamykane, ale system nie gwarantuje, że sesje spoza zakresu zostaną zamknięte. Dlatego zalecamy jawne wywoływanie metod close()
.
Wysyłanie wiadomości
Aby wysłać wiadomość na inne urządzenie, potrzebujesz tych obiektów:
DiscoverySession
. Ten obiekt umożliwia wywołaniesendMessage()
. Twoja aplikacja otrzymaDiscoverySession
, gdy opublikujesz usługę lub zasubskrybujesz ją.PeerHandle
drugiego urządzenia, aby przekierować wiadomość. Twoja aplikacja pobiera danePeerHandle
z innego urządzenia na 1 z 2 sposobów:- Aplikacja publikuje usługę i otrzymuje wiadomość od subskrybenta.
Aplikacja pobiera
PeerHandle
subskrybenta z wywołania zwrotnegoonMessageReceived()
. - Aplikacja subskrybuje usługę. Następnie, gdy znajdzie pasującego wydawcę, otrzyma jego
PeerHandle
z wywołania zwrotnegoonServiceDiscovered()
.
- Aplikacja publikuje usługę i otrzymuje wiadomość od subskrybenta.
Aplikacja pobiera
Aby wysłać wiadomość, zadzwoń pod numer sendMessage()
. Mogą wystąpić te wywołania zwrotne:
- Gdy połączenie zostanie odebrane, system wywołuje wywołanie zwrotne
onMessageSendSucceeded()
w aplikacji do wysyłania. - Gdy peer otrzyma wiadomość, system wywołuje wywołanie zwrotne
onMessageReceived()
w aplikacji odbierającej.
Chociaż PeerHandle
jest wymagany do komunikacji z peerami, nie należy traktować go jako stałego identyfikatora grupy porównawczej. Identyfikatory wyższego poziomu mogą być używane przez aplikację osadzone w usłudze wykrywania lub w kolejnych wiadomościach. Identyfikator możesz umieścić w usłudze wykrywania za pomocą metody setMatchFilter()
lub setServiceSpecificInfo()
PublishConfig
bądź SubscribeConfig
. Metoda setMatchFilter()
wpływa na wykrywanie, a metoda setServiceSpecificInfo()
nie.
Umieszczenie identyfikatora w wiadomości oznacza zmodyfikowanie tablicy bajtów wiadomości w taki sposób, aby zawierała identyfikator (na przykład na kilka pierwszych bajtów).
Tworzenie połączenia
Funkcja Wi-Fi Aware obsługuje sieć klient-serwer między dwoma urządzeniami Wi-Fi Aware.
Aby skonfigurować połączenie klient-serwer:
Za pomocą wykrywania przez Wi-Fi Aware możesz opublikować usługę (na serwerze) i zasubskrybować usługę (w kliencie).
Gdy subskrybent znajdzie wydawcę, wyślij do niego wiadomość.
Uruchom instancję
ServerSocket
na urządzeniu wydawcy i ustaw lub pobierz jej port:Kotlin
val ss = ServerSocket(0) val port = ss.localPort
Java
ServerSocket ss = new ServerSocket(0); int port = ss.getLocalPort();
Użyj metody
ConnectivityManager
, aby zażądać od wydawcy dostępu do sieci Wi-Fi Aware za pomocą metodyWifiAwareNetworkSpecifier
, określając sesję wykrywania iPeerHandle
subskrybenta, które zostały uzyskane z wiadomości przesyłanej przez subskrybenta: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);
Gdy wydawca poprosi o dostęp do sieci, powinien wysłać wiadomość do subskrybenta.
Gdy subskrybent otrzyma wiadomość od wydawcy, poproś go o dostęp do sieci Wi-Fi Aware, używając tej samej metody co w przypadku wydawcy. Podczas tworzenia
NetworkSpecifier
nie określaj portu. Odpowiednie metody wywołania zwrotnego są wywoływane, gdy połączenie sieciowe jest dostępne, zmienione lub utracone.Po wywołaniu metody
onAvailable()
u subskrybenta dostępny jest obiektNetwork
, za pomocą którego możesz otworzyć obiektSocket
, aby komunikować się zServerSocket
u wydawcy. Musisz jednak znać adres IPv6 i port protokołu IPv6.ServerSocket
Uzyskujesz te wyniki z obiektuNetworkCapabilities
podanego w wywołaniu zwrotnymonCapabilitiesChanged()
: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);
Po zakończeniu połączenia sieciowego wywołaj
unregisterNetworkCallback()
.
Duża liczba podobnych odbiorców i odkrywanie z uwzględnieniem lokalizacji
Urządzenie z lokalizacją RTT przez Wi-Fi może bezpośrednio mierzyć odległość od sieci równorzędnych i używać tych informacji do ograniczania wykrywania usług przez Wi-Fi.
Interfejs Wi-Fi RTT API umożliwia bezpośrednie przechodzenie do peera za pomocą sieci Wi-Fi Aware przy użyciu jego adresu MAC lub klasy PeerHandle.
Wykrywanie usług przez Wi-Fi można ograniczyć do wykrywania usług w obrębie określonej geofencingu. Możesz na przykład skonfigurować geofencing, który umożliwia wykrywanie urządzenia publikującego usługę "Aware_File_Share_Service_Name"
na odległość nie większą niż 3 metry (3000 mm) ani 10 metrów (10 000 mm).
Aby włączyć geofencing, wydawca i subskrybent muszą wykonać odpowiednie czynności:
Wydawca musi włączyć określanie zakresu w opublikowanej usłudze za pomocą metody setRangingEnabled(true).
Jeśli wydawca nie włączy zakresu, wszystkie ograniczenia geofencingu określone przez subskrybenta będą ignorowane, a wykrywanie będzie przebiegać normalnie z pominięciem odległości.
Subskrybent musi określić strefę geofencingu przy użyciu kombinacji atrybutów setMinDISTANCEMm i setMaxRangeMm.
W przypadku każdej z tych wartości nieokreślona odległość oznacza brak limitu. Tylko określenie maksymalnej odległości oznacza minimalną odległość 0. Tylko określenie minimalnej odległości nie oznacza, że nie ma maksymalnego.
Po wykryciu usługi równorzędnej w obrębie geofencingu uruchamiane jest wywołanie zwrotne onServiceDiscoveredWithinRange, które dostarcza zmierzoną odległość do peera. Interfejs bezpośredniego połączenia Wi-Fi RTT API może zostać wywołany w razie potrzeby do pomiaru odległości w późniejszych momentach.