Bloga dön
Transport19 min read

Wi-Fi Aware Taşıma Tasarımı — Komşu Keşfi ve Veri Yolları

Bu makale, PlainApp'in eş taşıma yedek zincirinin orta katmanı olarak, LAN (aynı alt ağ HTTPS) ve BLE (son çare GATT RPC) arasında Wi-Fi Aware'i (NAN — Neighbor Awareness Networking) nasıl kullandığını açıklamaktadır. Wi-Fi Aware, iki PlainApp cihazının farklı SSID'lerde, misafir ve IoT VLAN'larında veya hiç Wi-Fi altyapısı yokken — bir DHCP sunucusundan hiçbir zaman IP adresi gerektirmeden konuşmasını sağlayan şeydir.

Makale, yalnızca Android olan Aware oturum yaşam döngüsünü, yayımlama / abone olma keşif modelini, her iki tarafta da requestNetwork'ü çerçevenin ~500 ms penceresi içinde senkronize eden iki aşamalı rol bölünmesi el sıkışmasını, boşta süpürme ile eş başına bağlantı havuzunu, tek bir OkHttp istemcisinin hem LAN hem de Aware'e hizmet etmesini sağlayan IPv6 + özel-DNS hilesini ve BLE üzerinden eş Aware başlatmayı tetikleyen ön ısıtıcıyı ele almaktadır.

Daha geniş yedek zinciri için Sohbet Mimarisi'ne bakın. Aware kullanılamadığında devralan BLE taşıması için BLE Taşıması'na bakın. Aware PMK olarak yeniden kullanılan paylaşılan ChaCha20 anahtarının nasıl kurulduğu için Eşleştirme Akışı'na bakın.

İçindekiler

Neden Wi-Fi Aware? {#why-wi-fi-aware}

Wi-Fi Aware (IEEE 802.11bc, eski adıyla NAN — Neighbor Awareness Networking), iki cihazın herhangi bir Wi-Fi altyapısı olmadan birbirini keşfetmesine ve veri alışverişinde bulunmasına izin veren bir Wi-Fi Alliance sertifikasıdır — AP, yönlendirici, DHCP yok. PlainApp, LAN'ın kapsayamadığı iki senaryo için bunu kullanır:

  • Farklı SSID'ler / VLAN'lar. Misafir ağındaki bir telefon ve IoT VLAN'ındaki bir dizüstü bilgisayar, Wi-Fi üzerinden her ikisi de «çevrimiçi» ama birbirlerinin IP'sine ulaşamıyor. Aware, altyapıyı tamamen atlayan doğrudan cihazdan-cihaza bir veri yolu oluşturur.
  • Hiç altyapı yok. Wi-Fi açık ama AP olmayan ıssız bir yerdeki iki cihaz hala sohbet edebilir. (BLE de bunu kapsar, ancak Aware çok daha hızlıdır — ~10 ms gidiş-dönüşler ve saniyeler yerine MB/s.)

Diagram 1
1

Platform kısıtlamaları

Wi-Fi Aware, PlainApp'te yalnızca Android'dir:

  • Android 13 (API 33) minimumdur — PlainApp'in bağımlı olduğu setPort() ve setPmk() aşırı yüklerine sahip WifiAwareNetworkSpecifier.Builder, isTPlus() gerektirir.
  • iOS, Wi-Fi Aware'i üçüncü taraf uygulamalara sunmaz. iOS PlainApp, LAN'dan doğrudan BLE'ye düşer; WifiAwareTransport nesnesi iOS hedefine derlenmez bile (@RequiresApi(Build.VERSION_CODES.S) + androidMain kaynak kümesi).

Bu nedenle PeerTransportRouter.buildList, createWifiAwareTransport()'u çağırır — iOS'ta null döndüren bir fabrika.

Aware Yedek Zincirinde Nerede Yer Alır {#where-aware-sits-in-the-fallback-chain}

PlainApp'in PeerTransportRouter sıralı bir listedir. Her send veya downloadFile çağrısı için, listeyi yürür ve biri başarılı olana kadar her taşımayı dener; başarısızlıklar aşağı doğru kademeli olur.

Diagram 2
2

Aware neden «orta» ve «ilk» değil?

Çünkü LAN mevcut olduğunda neredeyse her zaman daha hızlıdır. Bir AP üzerinden aynı alt ağ Wi-Fi atlama, tek bir 802.11 çerçeve alışverişidir; bir Aware veri yolu, bir NDP kurulumu (ilk kullanımda ~5 s) artı cihazdan-cihaza bağlantı için ikinci bir Wi-Fi radyo bağlamı ekler. Her ikisi de ulaşılabilirse, LAN gecikme ve verimlilik açısından kazanır.

Buna karşılık, BLE her zaman daha yavaştır — ancak her iki cihaz eşleştirilmişse çalışır. Aware ortadadır: BLE'den daha hızlı, LAN'dan daha yavaş ve yalnızca Wi-Fi açık Android 13+ cihazlarda kullanılabilir.

Oturum Yaşam Döngüsü: Attach → Yayımla + Abone Ol {#session-lifecycle-attach--publish--subscribe}

Bir Wi-Fi Aware oturumu süreç geneldir. Cihaz başına tam olarak bir WifiAwareSession vardır; bunun içinde PlainApp bir yayımlama oturumu (eşler bizi keşfedebilsin) ve bir abone olma oturumu (eşleri keşfedebilelim) çalıştırır. Her ikisi de AwareSession.start(), attach callback'ini tamamladığı an başlatılır.

Diagram 3
3

Neden aynı cihazda hem yayımla hem abone ol?

Wi-Fi Aware keşif modeli asimetriktir: bir yayıncı bir hizmet reklam eder, bir abone onu tarar. Keşfi simetrik yapmak için (her iki cihaz da birbirini keşfeder), PlainApp ikisini birden aynı anda yapar. Bu olmadan, cihaz A'nın belirli bir eş için yayıncı mı yoksa abone mi olduğunu önceden bilmesi gerekir — ancak eş rolleri daha sonra clientId karşılaştırmasıyla belirlenir (bkz. Keşif ve Rol Ataması).

Aynı anda yayımlama ve abone olma, her cihazın diğerinin onServiceDiscovered'ını (abone olarak) görmesi VE diğerinin hello mesajlarını (yayıncı olarak) alması anlamına gelir — el sıkışmanın her iki yönü de her zaman kullanılabilir.

Sonlandırmada otomatik yeniden başlatma

Bazı Android varyantları (özellikle MIUI), pilden tasarruf etmek için uzun süredir çalışan Aware oturumlarını öldürür. PlainApp bunu onSessionTerminated callback'lerinde ele alır: sonlandırılan oturumu null yapar ve hala bağlı olan WifiAwareSession üzerinde hemen publishOwnService / subscribeOwnService'i tekrar çağırır. Attach oturumunun kendisi kaybolmaz — yalnızca yayımlama/abone olma keşif oturumu. Sonlandırmadan önceki eş tutamaçları bayat hale gelir, bu nedenle awaitPeerHandle, discoveredAt zaman damgasını kontrol eder ve 30 s'den eski tutamaçları atar.

Keşif ve Rol Ataması {#discovery--role-assignment}

Wi-Fi Aware veri yolu protokolü, bir tarafın yayıncı (sunucu) ve diğerinin abone (istemci) olarak hareket etmesini gerektirir. Her iki taraf aynı anda başlatıcı olamaz — çerçeve, eşleşen bir karşı taraf olmadan istekleri reddeder.

PlainApp, rolleri clientIds'in basit bir leksikografik karşılaştırmasıyla eş başına deterministik olarak atar:

Diagram 4
4

Neden müzakere yerine deterministik?

Müzakere edilen bir yaklaşım (örneğin «düşük MAC sunucudur») ekstra bir mesaj alışverişi gerektirir. Leksikografik karşılaştırma idempotent, simetrik ve durumsuzdur: her iki cihaz da herhangi bir iletişim olmadan aynı çift için aynı rolü hesaplar. clientId 13 karakterlik kısa bir UUID'dir, bu nedenle krizler (clientId == peer.id) yalnızca bir eşi kendisiyle karşılaştırırken oluşur — bu, hiçbir zaman taşımaya ulaşmaz.

Rol, aşağı yönlü iki şeyi belirler:

  1. Yeniden deneme döngüsünü kim yürütür. Yalnızca istemci requestNetwork'ü yeniden dener; sunucu, alınan her hello için tam olarak bir deneme yapar. Bu, 500 ms pencere için kritiktir (sonraki bölüm).
  2. Bağlantı noktasını kim ayarlar. Yayıncı, HTTPS sunucu bağlantı noktasında gelen bağlantıları kabul eden taraf olduğu için setPort(httpsPort) çağırır. Abone bağlantı noktası ayarlamaz — veri yolu kurulduktan sonra WifiAwareNetworkInfo'dan eşin bağlantı noktasını öğrenir.

İki Aşamalı El Sıkışma (hello + ready) {#the-two-phase-handshake-hello--ready}

Wi-Fi Aware veri yolu kurulumunun en zor kısmı zamanlamadır. Çerçeve, her iki tarafın da connectivityManager.requestNetwork'ü kabaca birbirlerinin 500 ms'i içinde çağırmasını gerektirir — bir taraf, karşı taraf eşleşen isteğini kaydetmeden önce çağırırsa, çerçeve hemen onUnavailable ile reddeder ("releaseRequestAsUnfulfillableByAnyFactory").

PlainApp bunu, Aware L2 mesaj kanalının üzerinde (onServiceDiscovered tarafından kullanılan aynı sendMessage API'si) çalışan iki mesajlık uygulama katmanı el sıkışmasıyla çözer:

Diagram 5
5

Neden tek mesaj yerine iki mesaj (hello + ready)?

Yalnızca hello, yön asimetrisi nedeniyle yeterli değildir. Abone, yayıncıyı keşfettiği an (onServiceDiscovered içinde) hello gönderebilir, ancak yayıncı, abonenin PeerHandle'ını alana kadar — ki bunu yalnızca hello'yu alarak öğrenir — requestNetwork'ü başlatamaz. Bu nedenle hello iki amaca hizmet eder:

  1. Abonenin PeerHandle'ını yayıncıya teslim et. Yayıncının, WifiAwareNetworkSpecifier'i oluşturmak için buna ihtiyacı vardır.
  2. Bağlanma niyetini bildir. Hello'yu almak, yayıncıya «abone requestNetwork yapacak, bu yüzden ben de yapmalıyım» söyler.

ready makbuzu ters yön için vardır — aboneye «yayıncı requestNetwork'ünü kaydetti» söylemek. Bu olmadan, abonenin requestNetwork'ü yayıncınınkinin önüne geçebilir ve çerçeve tarafından reddedilebilir. ready makbuzu engellemesiz bir sinyaldir: abone, requestNetwork çağırmadan önce onu beklemez (bu bir gidiş-dönüş eklerdi), ancak abone IDLE durumundayken (yeniden deneme girişimleri arasında) gelirse, abone RETRY_DELAY_MS boşluğunu beklemeden hemen yeniden deneyebilir.

Yeniden deneme döngüsü asimetrisi

Bu tasarımın en ince kısmıdır. Yalnızca abone yeniden dener. Yayıncı, alınan her hello için tam olarak bir requestNetwork denemesi yapar. Bunun nedeni:

  • Her iki taraf da bağımsız olarak yeniden deneseydi, yeniden deneme döngüleri faz dışına kayardı (farklı delay() süreleri, farklı GC duraklamaları) ve iki requestNetwork çağrısı 500 ms penceresi içinde nadiren çakışırdı.
  • Abonenin yeniden deneme döngüsü her denemede taze bir hello gönderir, bu da publishHelloListeners aracılığıyla yayıncının buildLink'ini yeniden tetikler. Bu, yayıncının requestNetwork'ünün her zaman hello'dan ~50 ms sonra gelmesini garanti eder, 500 ms penceresinin iyi içinde.

Bu, AwarePeerLink.build içinde ayrıntılı olarak belgelenmiştir.

NDP requestNetwork — 500 ms Penceresi {#ndp-requestnetwork--the-500-ms-window}

requestNetwork çağrısı, Aware taşımasındaki en zamana duyarlı işlemdir. Her tarafta olanlar şunlardır:

Diagram 6
6

onUnavailable callback'i ne anlama gelir

onUnavailable, çerçeve eşleşen bir eş isteği bulamadan önce requestNetwork'ü reddettiğinde ateşlenir. PeerHandle'ın kendisi hala geçerlidir — yalnızca NDP (Neighbor Discovery Protocol) eşleşmesi, diğer taraf henüz kaydetmediği için başarısız oldu. PlainApp bu durumda kasıtlı olarak session.invalidatePeerHandle çağırmaz, çünkü tutamacı geçersiz kılmak, onServiceDiscovered'ın hiç çağrılmadığı tek sinyali atardı (abone olma oturumu ömrü boyunca eş başına bir kez ateşlenir). Tutamac korunduğunda, yeniden deneme, taze bir keşfi beklemek yerine onu yeniden kullanabilir.

Aynı şey onMessageReceived'dan yayıncı tarafı tutamacı için de geçerlidir — yayıncı, publishPeerHandles[fromCid] girişini başarısız denemeler arasında tutar, böylece abonenin bir sonraki hello'su önbelleğe alınmış tutamacı yeniden kullanır, bırakılmaz.

Her eşleştirilmiş eş, süreç geneli AwareLinkPool'a ait kendi AwarePeerLink nesnesini alır. Havuz, keşif olaylarını, bağlantı yeniden kullanımını ve boşta çıkarmayı yönetir.

Diagram 7
7

Neden keşifte otomatik kurulum yok?

Havuz, onServiceDiscovered ateşlendiğinde açıkça bir bağlantı kurmaz. Bu kritik bir karardır: meşgul bir kafe, «plain-peer» hizmetini yayımlayan 100 PlainApp cihazına sahip olabilir. Her keşif bir requestNetwork tetikleseydi, çerçeve NDP kurulum girişimleriyle dolardı ve Wi-Fi radyosu doygunluğa ulaşırdı.

Bunun yerine, havuz yalnızca PeerHandle'ı kaydeder ve şunlardan birini bekler:

  1. Yerel kullanıcı bir mesaj gönderirWifiAwareTransport.sendpool.buildLink(peer) (gönderen tarafı tetikleyici).
  2. Uzak eş bir hello gönderironPublishHelloReceivedbuildLink(peer) (alıcı tarafı tetikleyici).
  3. Uzak eş bir ready gönderironSubscribeReadyReceivedbuildLink(peer) (alıcı tarafı tetikleyici).

Bu şekilde, bağlantılar yalnızca kullanıcının gerçekten mesaj alışverişi yaptığı eşler için kurulur — radyo menzilindeki her PlainApp cihazı için değil.

Boşta süpürme

Her 10 saniyede bir, havuz tüm bağlantıları yürür ve lastActiveAt'i 60 saniyeden eski olanları kapatır. Her send ve downloadFile, zaman damgasını yenilemek için link.touch() çağırır. Bu, kullanıcının sohbet etmeyi bıraktığı eşlar için Wi-Fi radyo bağlamını ve OkHttp bağlantı havuzunu geri kazanır — önemli çünkü Android, eşzamanlı Aware veri yollarının sayısını kabaca 4–10 ile sınırlar (cihaza bağlı).

IPv6 Adresleme ve plain-aware-peer DNS Hilesi {#ipv6-addressing--the-plain-aware-peer-dns-trick}

Wi-Fi Aware veri yolları yalnızca bağ-yerel IPv6 kullanır. IPv4, DNS sunucusu, DHCP yok. Eşin IPv6 adresi, onCapabilitiesChanged içinde WifiAwareNetworkInfo.peerIpv6Addr alanı üzerinden teslim edilir — yalnızca Aware ağ arayüzünde anlamlı olan bir fe80::... adresi.

PlainApp'in bu adrese HTTPS istekleri göndermesi gerekir, ancak OkHttp'ın https:// URL ayrıştırması, bir ana bilgisayar adında ham IPv6 değişmez değerlerini reddeder (https://[fe80::abcd]:8443/ çalışır, ancak bunu özel bir Dns çözücüsü üzerinden yönlendirmek daha temizdir). Püf noktası:

Diagram 8
8

Neden bekçi ana bilgisayar adı?

Alternatif — IPv6 değişmez değerini doğrudan URL'de iletmek — her çağrı yerinin bağ-yerel adresi hakkında bilmesini gerektirir. Bir bekçi ana bilgisayar adı kullanarak, URL yapımı LAN ve Aware için aynıdır: her ikisi de OkHttp'ın ayrıştırabileceği geçerli bir https://<host>:<port>/peer_graphql URL'si üretir. Tek fark, istemciye bağlanan Dns uygulamasıdır — LAN sistem DNS'sini kullanır, Aware, bekçi ana bilgisayar adı için önbelleğe alınmış bağ-yerel adresi döndüren ve başka her şey için Dns.SYSTEM'e düşen awareDns(peerIpv6) kullanır.

Neden network.socketFactory?

Android'in Network nesnesi belirli bir ağ arayüzünü temsil eder (bu durumda, Aware veri yolu). network.socketFactory çağırarak ve onu OkHttp'ın socketFactory yapılandırmasına geçirerek, tüm TCP soketlerinin Aware arayüzünde oluşturulmasını zorlarız — varsayılan Wi-Fi veya hücresel arayüzde değil. Bu olmadan, işletim sistemi isteği varsayılan ağ üzerinden yönlendirirdi; burada bağ-yerel IPv6 ulaşılamaz ve istek ENETUNREACH ile başarısız olurdu.

Kriptografi: PMK Türetme ve ChaCha20 Yeniden Kullanımı {#cryptography-pmk-derivation--chacha20-reuse}

Wi-Fi Aware, veri yolu için isteğe bağlı bir PMK (Pairwise Master Key) destekler. Ayarlandığında, L2 bağlantının kendisi o PMK ile şifrelenir — Wi-Fi radyo şifrelemeyi yönetir, uygulama katmanı kripto gerekmez.

PlainApp, PMK'yi LanTransport ve BleTransport'in uygulama katmanı şifrelemesi için kullandığı aynı ChaCha20 paylaşılan anahtarından türetir:

Diagram 9
9

Neden 32 bayta kırpmak?

Wi-Fi Aware PMK tam olarak 32 bayt (256 bit) olmalıdır. Eşleştirmeden gelen ChaCha20 paylaşılan anahtarı da normal durumda 32 bayttır, bu nedenle raw.size == 32 dalı yaygın yoldur. Kırpmaz/doldurma yedekliği, anahtarın daha kısa saklandığı (teorik) durumu ele alır — 32 bayta sıfırlarla doldurma savunmacı bir önlemdir, uygulamada düzgün eşleştirilmiş eşlerle olan bir şey değildir.

İmzalı zarf LAN ile aynıdır

createCryptoHttpClient, LanTransport tarafından kullanılan aynı fabrika olduğundan, Aware üzerindeki L7 kripto, LAN ile bayt bayt aynıdır. Sunucu taraflı PeerGraphQLService, isteği hangi taşımanın teslim ettiğini bilmez (veya umursamaz) — yalnızca imzalı, şifreli bir GraphQL yükü görür ve eşin paylaşılan anahtarıyla şifresini çözer. Bu, Sohbet Mimarisi'nde belgelenen «tek kod tabanı, birçok taşıma» ilkesidir.

Mesaj Gönderme Yolu (Uçtan Uca) {#message-send-path-end-to-end}

Hepsini bir araya getirirsek — Wi-Fi Aware üzerinden bir sohbet mesajı gönderildiğinde ne olur:

Diagram 10
10

Önemli tasarım seçimleri

  • Bağlantı yeniden kullanımı. Her istekten sonra GATT bağlantısını yıkan BleTransport'ın aksine, WifiAwareTransport, kullanıcı 60 s boşta penceresi içinde yaptığı kadar çok istek için Aware veri yolunu yeniden kullanır. İlk istek ~400 ms el sıkışmasını öder; sonraki istekler ~10 ms gidiş-dönüşlerdir.
  • LAN ile aynı kripto. ChaCha20 interceptor'ü ve imzalı zarf, LAN ile bayt bayt aynıdır. Eşin PeerGraphQLService'i isteği hangi taşımanın teslim ettiğini bilmez.
  • Bağlantı başarısızlığında önleme yok. buildLink başarısız olursa, taşıma TransportUnavailable fırlatır ve yönlendirici BLE'ye düşer. send içinde yeniden deneme yoktur — AwarePeerLink.build zaten kendi iç yeniden deneme döngüsünü yapar (istemcide MAX_BUILD_ATTEMPTS = 1, ön ısıtıcı her iki tarafı da hazırlamışsa daha fazla).

Dosya İndirme Yolu (Uçtan Uca) {#file-download-path-end-to-end}

Aware üzerinden dosya indirmeleri, sohbet mesajlarıyla aynı veri yolunu yeniden kullanır, ancak büyük dosyaları akış için yapılandırılmış ayrı bir OkHttp istemcisi kullanır:

Diagram 11
11

Neden indirmeler için ayrı istemci?

Sohbet istemcisi (AwareHttpClientFactory.build), 30 s requestTimeoutMillis'e sahiptir — GraphQL mutasyonları için uygun ancak 100 MB'lık bir dosya indirmesi için felaket. İndirme istemcisi (buildFileDownload) şunları ayarlar:

  • connectTimeoutMillis = 10_000 (sohbetin 5 s'inden daha uzun, taze bir veri yolu üzerinde yavaş ilk pakete daha hoşgörülü)
  • okuma başına readTimeout = 120 s (örtük 10 s varsayılanına kıyasla)
  • requestTimeoutMillis = 120_000 (2 dakika — çoğu dosya için yeterli)
  • retryOnConnectionFailure(true) — indirme ortasında düşen bir okuma, tüm transferi başarısız kılmak yerine yeniden denenir

Ayrıca ChaCha20 interceptor'ünü atlar. /fs uç noktası ham dosya baytları sunar (imzalı bir GraphQL zarfı değil) ve L2 PMK (mevcut olduğunda) zaten radyo bağlantısını şifreler. 50 MB'lık bir videoyu yazılımla ChaCha20 ile çift şifrelemek CPU'yu boşa harcar ve transferi yavaşlatır.

Akış, arabellekleme değil

BLE yolu gibi, Aware indirmeleri de dosyayı bir ByteReadChannel üzerinden akışlar — dosya, baytlar geldikçe geçici bir dosyaya yazılır, bellekte arabelleğe alınmaz. PeerFileDownloader 8 KB parçalar okur ve her saniye ilerleme olayları yayar. Aynı DownloadedResponse / PeerFileDownloader / DownloadQueue ardışık düzeni tüm taşımalar arasında yeniden kullanılır — taşımaya özel yalnızca channel kaynağıdır.

Ön Isıtma: BLE Tetikli Aware Başlatma {#prewarming-ble-triggered-aware-startup}

Aware yolundaki en büyük kullanıcı tarafından görülebilen gecikme, ilk el sıkışmadır — her iki taraf da henüz Aware'i başlatmadıysa, kullanıcının ilk mesajı şunlar için beklemek zorundadır:

  1. Yerel Aware oturum attach (~1 s)
  2. Yerel yayımla + abone ol başlangıcı (~1 s)
  3. Uzak eşin Aware başlatması (~2 s BLE üzerinden)
  4. Karşılıklı keşif (~1 s)
  5. NDP el sıkışması (~400 ms)

Bu, ilk bayt gönderilmeden önce ~5 saniyedir. Bu gecikmeyi gizlemek için PeerTransportPrewarmer, ChatPage girişinde çalışır ve uzak eşin Aware başlatmasını BLE üzerinden tetikler:

Diagram 12
12

BLE'nin çift rolü

BLE burada iki amaca hizmet eder:

  1. Eşin mevcut Aware durumunu oku (ucuz, GATT bağlantısı yok — tarama yanıtının serviceData byte0'ı Aware bayraklarını taşır).
  2. Destekliyor ama şu anda çalıştırmıyorsa eşin Aware'i başlatmasını tetikle. Bu, düzenli BleTransport.send yolundan geçer — paylaşılan ChaCha20 anahtarıyla şifrelenmiş bir startAware GraphQL mutasyonu, eşin /peer_graphql uç noktasına GATT RPC aracılığıyla teslim edilir.

Bu, taşımaların yalnızca yedeğe düştüğü yerine işbirliği yaptığı birkaç yerden biridir: BLE, kullanıcı farkına bile varmadan oturumu daha hızlı Aware taşımasına önleyici olarak yükseltmek için kullanılır.

Neden iyimser setAwareRunning(true)?

startAware mutasyonu, uzak eşin resolver'ı WifiAwareTransport.start()'ı çağırdığı anda başarı döndürür — ancak Aware oturumu henüz attach edilmemiştir (onAttached asenkron olarak ateşlenir). PlainApp, eşin awareRunning = true olarak iyimserce işaretler, çünkü:

  • Gerçekten başladıysa, bir sonraki send Aware kullanır (hızlı).
  • Başlamadıysa (örneğin eşin Wi-Fi kapalıysa), bir sonraki send'in buildLink'i TransportUnavailable ile başarısız olur ve doğal olarak BLE'ye düşer.
  • Yanlış pozitifin maliyeti bir ~5 s zaman aşımıdır, kalıcı bir blok değil — PeerCircuitBreaker başarısızlığı kaydeder ama BLE bacağını açmaz (BLE yalnızca kendi başarısızlıklarında açılır).

Neden 30 s kıstırma?

PeerTransportPrewarmer.prewarm(peerId), eş başına bir zaman damgası kaydeder ve 30 s içinde yeniden çalışmayı reddeder. Bunun nedeni, kullanıcının sohbet listesi ve sohbet sayfası arasında sık sık ileri geri gezinmesidir — kıstırlama olmadan, her gezinme bir BLE taraması + startAware mutasyonu tetikler, pili tüketir ve BLE radyosunu istila eder. 30 s penceresi, yeni çevrimiçi olmuş bir eşi yakalamak için yeterince kısa (örneğin kullanıcı uzak cihazda uygulamayı açtı) ancak sahte yeniden çalıştırmaları önlemek için yeterince uzundur.

Hata Modları ve Hızlı-Atlama Bayrağı {#failure-modes--the-fast-skip-flag}

Aware, herhangi bir taşımadan daha fazla hata moduna sahiptir. isAwareRunning hızlı-atlama bayrağı, tüm modüldeki en önemli optimizasyondur — olmadan, her send, BLE'ye düşmeden önce buildLink zaman aşımında 10 s israf ederdi.

Diagram 13
13

isAwareRunning bayrağı kilit taşı

Bu tek boolean olmadan, her Aware send ya:

  • Her zaman buildLink dener → Aware'i çalışmayan bir eşe her gönderimde 10 s zaman aşımı.
  • Her zaman Aware'i atla → her iki taraf da çalıştırken bile asla kullanma.

Bayrak, otorite sırasına göre iki kaynaktan yenilenir:

  1. BLE tarama yanıtı (ucuz, GATT bağlantısı yok) — PeerTransportPrewarmer.refreshAwareFlagFromScan tarafından ayarlanır. Eş, Aware durumunu 9 baytlık serviceData yükünde (byte0 bit alanı) reklam eder.
  2. GATT DISCOVER yanıtı (yetkili) — tam bir keşif olduğunda PairingTransport.scanAndDiscover tarafından ayarlanır. Bu, tarama ipucunu üzerine yazar.

Yanlış olduğunda, WifiAwareTransport.send ve downloadFile hemenTransportUnavailable fırlatır — tarama yok, el sıkışma yok, zaman aşımı yok. Yönlendirici mikro saniyeler içinde BLE'ye düşer.

Anahtar Sabitler Referansı {#key-constants-reference}

SabitDeğerNeredeAmaç
AwareSession.SERVICE_NAME"plain-peer"KeşifHer PlainApp cihazı tarafından yayımlanan ve abone olunan hizmet adı
AwareSession.PEER_HANDLE_MAX_AGE_MS30 000PeerHandle önbelleğiBayat tutamaçları at (eşin yayımlama oturumu yeniden başlatılmış olabilir)
AwareSession.READY_TIMEOUT_MS15 000El sıkışmaAbonenin yayıncının ready makbuzunu beklemesi
AwareSession.MSG_HELLO0El sıkışmaAbone → Yayıncı mesaj kimliği
AwareSession.MSG_READY1El sıkışmaYayıncı → Abone mesaj kimliği
AwarePeerLink.MAX_BUILD_ATTEMPTS1El sıkışma (yalnızca istemci)Tek deneme — 3'tü, şimdi 1 çünkü ön ısıtıcı her iki tarafı hazırlar
AwarePeerLink.ATTEMPT_TIMEOUT_MS5 000El sıkışmaDeneme başına zaman aşımı — 10 s'di, yedeklemeyi hızlandırmak için yarıya indirildi
AwarePeerLink.RETRY_DELAY_MS500El sıkışmaYeniden deneme girişimleri arasındaki gecikme (yalnızca istemci)
AwarePeerLink.REQUEST_TIMEOUT_MS30 000NDPconnectivityManager.requestNetwork zaman aşımı
AwareLinkPool.IDLE_TIMEOUT_MS60 000Havuz süpürme60 s hareketsizlikten sonra boşta bağlantıları kapat
AwareLinkPool.IDLE_SWEEP_INTERVAL_MS10 000Havuz süpürmeSüpürme aralığı
AwareHttpClientFactory.AWARE_HOST"plain-aware-peer"DNSÖzel Dns tarafından eş IPv6'sına çözülen bekçi ana bilgisayar adı
build sohbet istemcisiconnectTimeout 5 s, requestTimeout 30 s, ChaCha20 interceptor'ü
buildFileDownloadconnectTimeout 10 s, readTimeout 120 s, requestTimeout 120 s, kripto yok
PeerTransportPrewarmer.PREWARM_TTL_MS30 000Ön ısıtmaEş başına kıstırma
PeerTransportPrewarmer.BLE_SCAN_TIMEOUT_MS15 000Ön ısıtmarefreshAwareFlagFromScan için BLE tarama zaman aşımı
PeerCircuitBreaker.WINDOW_MS30 000Devre kesiciEşik sonrası açık süre
PeerCircuitBreaker.MAX_FAILURES2Devre kesiciAçmak için pencere içinde başarısızlık
TempData.httpsPort8443 (varsayılan)SunucuWifiAwareNetworkSpecifier.setPort ile yayımlanan yayıncı bağlantı noktası
BleServiceData.AWARE_SUPPORTED0x01BLE tarama yanıtıEşin Wi-Fi Aware desteklediğini gösteren bit
BleServiceData.AWARE_RUNNING0x02BLE tarama yanıtıEşin Aware hizmetinin şu anda çalıştığını gösteren bit

Tasarım Takasları Özeti {#design-trade-offs-recap}

Diagram 14
14

Daha Fazla Okuma {#further-reading}

  • Sohbet MimarisiWifiAwareTransport'in LAN → Aware → BLE yedek zincirine ve daha geniş sohbet gönderme/alma ardışık düzenine nasıl uyduğu.
  • BLE Taşıması — Aware kullanılamadığında devralan son çare taşıma; ayrıca ön ısıtıcının uzak eş üzerinde Aware başlatmayı tetiklemek için kullandığı kanal.
  • Eşleştirme Akışı — Aware PMK olarak yeniden kullanılan paylaşılan ChaCha20 anahtarının nasıl kurulduğu ve BLE tarama yanıt bayraklarının (AWARE_SUPPORTED / AWARE_RUNNING) nasıl doldurulduğu.