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?
- Aware Yedek Zincirinde Nerede Yer Alır
- Oturum Yaşam Döngüsü: Attach → Yayımla + Abone Ol
- Keşif ve Rol Ataması
- İki Aşamalı El Sıkışma (hello + ready)
- NDP requestNetwork — 500 ms Penceresi
- Eş Başına Bağlantı Havuzu ve Boşta Süpürme
- IPv6 Adresleme ve
plain-aware-peerDNS Hilesi - Kriptografi: PMK Türetme ve ChaCha20 Yeniden Kullanımı
- Mesaj Gönderme Yolu (Uçtan Uca)
- Dosya İndirme Yolu (Uçtan Uca)
- Ön Isıtma: BLE Tetikli Aware Başlatma
- Hata Modları ve Hızlı-Atlama Bayrağı
- Anahtar Sabitler Referansı
- Tasarım Takasları Özeti
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.)
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()vesetPmk()aşırı yüklerine sahipWifiAwareNetworkSpecifier.Builder,isTPlus()gerektirir. - iOS, Wi-Fi Aware'i üçüncü taraf uygulamalara sunmaz. iOS PlainApp, LAN'dan
doğrudan BLE'ye düşer;
WifiAwareTransportnesnesi 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.
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.
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:
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:
- 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). - 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 sonraWifiAwareNetworkInfo'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:
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:
- Abonenin PeerHandle'ını yayıncıya teslim et. Yayıncının,
WifiAwareNetworkSpecifier'i oluşturmak için buna ihtiyacı vardır. - 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 ikirequestNetworkç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
publishHelloListenersaracılığıyla yayıncınınbuildLink'ini yeniden tetikler. Bu, yayıncınınrequestNetwork'ü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:
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.
Eş Başına Bağlantı Havuzu ve Boşta Süpürme {#per-peer-link-pool--idle-sweeping}
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.
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:
- Yerel kullanıcı bir mesaj gönderir →
WifiAwareTransport.send→pool.buildLink(peer)(gönderen tarafı tetikleyici). - Uzak eş bir hello gönderir →
onPublishHelloReceived→buildLink(peer)(alıcı tarafı tetikleyici). - Uzak eş bir ready gönderir →
onSubscribeReadyReceived→buildLink(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ı:
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:
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:
Ö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.
buildLinkbaşarısız olursa, taşımaTransportUnavailablefırlatır ve yönlendirici BLE'ye düşer.sendiçinde yeniden deneme yoktur —AwarePeerLink.buildzaten kendi iç yeniden deneme döngüsünü yapar (istemcideMAX_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:
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:
- Yerel Aware oturum attach (~1 s)
- Yerel yayımla + abone ol başlangıcı (~1 s)
- Uzak eşin Aware başlatması (~2 s BLE üzerinden)
- Karşılıklı keşif (~1 s)
- 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:
BLE'nin çift rolü
BLE burada iki amaca hizmet eder:
- Eşin mevcut Aware durumunu oku (ucuz, GATT bağlantısı yok — tarama
yanıtının
serviceDatabyte0'ı Aware bayraklarını taşır). - Destekliyor ama şu anda çalıştırmıyorsa eşin Aware'i başlatmasını tetikle.
Bu, düzenli
BleTransport.sendyolundan geçer — paylaşılan ChaCha20 anahtarıyla şifrelenmiş birstartAwareGraphQL mutasyonu, eşin/peer_graphqluç 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
sendAware kullanır (hızlı). - Başlamadıysa (örneğin eşin Wi-Fi kapalıysa), bir sonraki
send'inbuildLink'iTransportUnavailableile 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 —
PeerCircuitBreakerbaş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.
isAwareRunning bayrağı kilit taşı
Bu tek boolean olmadan, her Aware send ya:
- Her zaman
buildLinkdener → 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:
- BLE tarama yanıtı (ucuz, GATT bağlantısı yok) —
PeerTransportPrewarmer.refreshAwareFlagFromScantarafından ayarlanır. Eş, Aware durumunu 9 baytlıkserviceDatayükünde (byte0 bit alanı) reklam eder. - GATT DISCOVER yanıtı (yetkili) — tam bir keşif olduğunda
PairingTransport.scanAndDiscovertarafı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}
| Sabit | Değer | Nerede | Amaç |
|---|---|---|---|
AwareSession.SERVICE_NAME | "plain-peer" | Keşif | Her PlainApp cihazı tarafından yayımlanan ve abone olunan hizmet adı |
AwareSession.PEER_HANDLE_MAX_AGE_MS | 30 000 | PeerHandle önbelleği | Bayat tutamaçları at (eşin yayımlama oturumu yeniden başlatılmış olabilir) |
AwareSession.READY_TIMEOUT_MS | 15 000 | El sıkışma | Abonenin yayıncının ready makbuzunu beklemesi |
AwareSession.MSG_HELLO | 0 | El sıkışma | Abone → Yayıncı mesaj kimliği |
AwareSession.MSG_READY | 1 | El sıkışma | Yayıncı → Abone mesaj kimliği |
AwarePeerLink.MAX_BUILD_ATTEMPTS | 1 | El 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_MS | 5 000 | El sıkışma | Deneme başına zaman aşımı — 10 s'di, yedeklemeyi hızlandırmak için yarıya indirildi |
AwarePeerLink.RETRY_DELAY_MS | 500 | El sıkışma | Yeniden deneme girişimleri arasındaki gecikme (yalnızca istemci) |
AwarePeerLink.REQUEST_TIMEOUT_MS | 30 000 | NDP | connectivityManager.requestNetwork zaman aşımı |
AwareLinkPool.IDLE_TIMEOUT_MS | 60 000 | Havuz süpürme | 60 s hareketsizlikten sonra boşta bağlantıları kapat |
AwareLinkPool.IDLE_SWEEP_INTERVAL_MS | 10 000 | Havuz süpürme | Sü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 istemcisi | connectTimeout 5 s, requestTimeout 30 s, ChaCha20 interceptor'ü | ||
buildFileDownload | connectTimeout 10 s, readTimeout 120 s, requestTimeout 120 s, kripto yok | ||
PeerTransportPrewarmer.PREWARM_TTL_MS | 30 000 | Ön ısıtma | Eş başına kıstırma |
PeerTransportPrewarmer.BLE_SCAN_TIMEOUT_MS | 15 000 | Ön ısıtma | refreshAwareFlagFromScan için BLE tarama zaman aşımı |
PeerCircuitBreaker.WINDOW_MS | 30 000 | Devre kesici | Eşik sonrası açık süre |
PeerCircuitBreaker.MAX_FAILURES | 2 | Devre kesici | Açmak için pencere içinde başarısızlık |
TempData.httpsPort | 8443 (varsayılan) | Sunucu | WifiAwareNetworkSpecifier.setPort ile yayımlanan yayıncı bağlantı noktası |
BleServiceData.AWARE_SUPPORTED | 0x01 | BLE tarama yanıtı | Eşin Wi-Fi Aware desteklediğini gösteren bit |
BleServiceData.AWARE_RUNNING | 0x02 | BLE tarama yanıtı | Eşin Aware hizmetinin şu anda çalıştığını gösteren bit |
Tasarım Takasları Özeti {#design-trade-offs-recap}
Daha Fazla Okuma {#further-reading}
- Sohbet Mimarisi —
WifiAwareTransport'inLAN → Aware → BLEyedek 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.