Bài viết đề cập đến vòng đời phiên Aware chỉ trên Android, mô hình khám phá
publish / subscribe, bắt tay hai pha phân vai trò đồng bộ hóa
requestNetwork ở cả hai bên trong cửa sổ ~500 ms của khung làm việc, nhóm
kết nối per-peer với quét nhàn rỗi, thủ thuật IPv6 + DNS tùy chỉnh cho phép
một OkHttp client duy nhất phục vụ cả LAN và Aware, và bộ làm nóng trước
kích hoạt khởi động Aware của peer qua BLE.
Để tìm hiểu về chuỗi dự phòng rộng hơn, xem Chat Architecture. Để tìm hiểu về tầng vận chuyển BLE tiếp quản khi Aware không khả dụng, xem BLE Transport. Để tìm hiểu cách khóa ChaCha20 chung được tái sử dụng làm Aware PMK được thiết lập, xem Pairing Flow.
Mục lục
- Tại sao Wi-Fi Aware?
- Vị trí của Aware trong chuỗi dự phòng
- Vòng đời phiên: Attach → Publish + Subscribe
- Khám phá và gán vai trò
- Bắt tay hai pha (hello + ready)
- NDP requestNetwork — Cửa sổ 500 ms
- Nhóm kết nối per-peer và quét nhàn rỗi
- Định địa chỉ IPv6 và thủ thuật DNS
plain-aware-peer - Mật mã: Dẫn xuất PMK và tái sử dụng ChaCha20
- Đường dẫn gửi tin nhắn (End-to-End)
- Đường dẫn tải tệp (End-to-End)
- Làm nóng trước: Khởi động Aware được kích hoạt qua BLE
- Chế độ lỗi và cờ hiệu bỏ qua nhanh
- Tham chiếu hằng số chính
- Tóm tắt đánh đổi thiết kế
Tại sao Wi-Fi Aware? {#why-wi-fi-aware}
Wi-Fi Aware (IEEE 802.11bc, trước đây là NAN — Neighbor Awareness Networking) là chứng nhận của Wi-Fi Alliance cho phép hai thiết bị khám phá lẫn nhau và trao đổi dữ liệu mà không cần bất kỳ cơ sở hạ tầng Wi-Fi nào — không AP, không router, không DHCP. PlainApp sử dụng nó cho hai tình huống mà LAN không thể đề cập đến:
- SSID / VLAN khác nhau. Một điện thoại trên mạng khách và một laptop trên VLAN IoT đều "online" qua Wi-Fi nhưng không thể truy cập IP của nhau. Aware tạo một đường dẫn dữ liệu thiết bị-đến-thiết bị trực tiếp bỏ qua hoàn toàn cơ sở hạ tầng.
- Hoàn toàn không có cơ sở hạ tầng. Hai thiết bị ngoài vùng hoang dã với Wi-Fi bật nhưng không có AP vẫn có thể trò chuyện. (BLE cũng đề cập đến trường hợp này, nhưng Aware nhanh hơn nhiều — ~10 ms mỗi round trip so với vài giây, và MB/s so với vài chục KB/s.)
Ràng buộc nền tảng
Wi-Fi Aware là chỉ trên Android trong PlainApp:
- Android 13 (API 33) là tối thiểu — các overload
WifiAwareNetworkSpecifier.BuildervớisetPort()vàsetPmk()mà PlainApp phụ thuộc yêu cầuisTPlus(). - iOS không phơi bày Wi-Fi Aware cho ứng dụng bên thứ ba. PlainApp trên iOS
dự phòng trực tiếp từ LAN xuống BLE; đối tượng
WifiAwareTransportthậm chí không được biên dịch vào target iOS (@RequiresApi(Build.VERSION_CODES.S)+ source set androidMain).
Đó là lý do PeerTransportRouter.buildList gọi
createWifiAwareTransport() — một factory trả về null trên iOS.
Vị trí của Aware trong chuỗi dự phòng {#where-aware-sits-in-the-fallback-chain}
PeerTransportRouter của PlainApp là một danh sách có thứ tự. Cho mỗi lệnh
gọi send hoặc downloadFile, nó duyệt qua danh sách và thử từng tầng vận
chuyển cho đến khi một cái thành công; các lỗi trượt xuống dưới.
Tại sao Aware là "tầng giữa" chứ không phải "tầng đầu tiên"?
Vì LAN hầu như luôn nhanh hơn khi khả dụng. Một điểm nhảy Wi-Fi cùng subnet qua AP là một trao đổi frame 802.11 đơn lẻ; một đường dẫn dữ liệu Aware thêm một NDP setup (~5 s lần đầu sử dụng) cộng thêm một ngữ cảnh radio Wi-Fi thứ hai cho liên kết thiết bị-đến-thiết bị. Nếu cả hai đều tiếp cận được, LAN thắng về độ trễ và thông lượng.
Ngược lại, BLE luôn luôn chậm hơn — nhưng hoạt động bất cứ khi nào hai thiết bị đã pair. Aware nằm ở giữa: nhanh hơn BLE, chậm hơn LAN, và chỉ khả dụng trên thiết bị Android 13+ có bật Wi-Fi.
Vòng đời phiên: Attach → Publish + Subscribe {#session-lifecycle-attach--publish--subscribe}
Một phiên Wi-Fi Aware là toàn tiến trình. Có chính xác một
WifiAwareSession mỗi thiết bị; trong đó, PlainApp chạy một phiên
publish (để peer có thể khám phá chúng ta) và một phiên subscribe
(để chúng ta có thể khám phá peer). Cả hai đều được bắt đầu ngay khi
AwareSession.start() hoàn thành callback attach.
Tại sao publish VÀ subscribe trên cùng thiết bị?
Mô hình khám phá Wi-Fi Aware là bất đối xứng: nhà xuất bản quảng bá một
dịch vụ, người đăng ký quét để tìm nó. Để làm cho khám phá đối xứng (cả hai
thiết bị khám phá lẫn nhau), PlainApp làm cả hai cùng lúc. Nếu không,
thiết bị A sẽ phải biết trước liệu nó là nhà xuất bản hay người đăng ký
cho một peer nhất định — nhưng vai trò peer được xác định sau bằng so sánh
clientId (xem Khám phá và gán vai trò).
Publish và subscribe đồng thời nghĩa là mỗi thiết bị thấy
onServiceDiscovered của bên kia (làm người đăng ký) VÀ nhận tin nhắn
hello của bên kia (làm nhà xuất bản) — cả hai hướng của bắt tay đều luôn
khả dụng.
Tự động khởi động lại khi kết thúc
Một số biến thể Android (đặc biệt MIUI) kết thúc các phiên Aware chạy lâu
để tiết kiệm pin. PlainApp xử lý điều này trong callback
onSessionTerminated: nó đặt null phiên bị kết thúc và ngay lập tức gọi
publishOwnService / subscribeOwnService lại trên WifiAwareSession
vẫn còn attached. Bản thân phiên attach không bị mất — chỉ có phiên khám
phá publish/subscribe. Các peer handle từ trước khi kết thúc trở nên cũ,
đó là lý do awaitPeerHandle kiểm tra timestamp discoveredAt và loại
bỏ các handle cũ hơn 30 s.
Khám phá và gán vai trò {#discovery--role-assignment}
Giao thức đường dẫn dữ liệu Wi-Fi Aware yêu cầu một bên đóng vai trò nhà xuất bản (server) và bên kia làm người đăng ký (client). Cả hai bên không thể đồng thời là bên khởi tạo — khung làm việc từ chối các yêu cầu không có counterpart phù hợp.
PlainApp gán vai trò xác định cho từng peer sử dụng so sánh từ điển đơn giản clientIds:
Tại sao xác định chứ không thương lượng?
Một cách tiếp cận thương lượng (ví dụ "MAC thấp hơn là server") sẽ yêu cầu
một trao đổi tin nhắn thêm. So sánh từ điển là idempotent, đối xứng, và
không trạng thái: cả hai thiết bị tính toán cùng vai trò cho cùng pair
mà không cần giao tiếp. clientId là một UUID ngắn 13 ký tự, nên hòa
(clientId == peer.id) chỉ xảy ra khi so sánh peer với chính nó — không
bao giờ đến tầng vận chuyển.
Vai trò xác định hai thứ downstream:
- Ai điều khiển vòng lặp retry. Chỉ client retry
requestNetwork; server thực hiện chính xác một nỗ lực mỗi hello nhận được. Điều này quan trọng cho cửa sổ 500 ms (phần kế tiếp). - Ai đặt port. Nhà xuất bản gọi
setPort(httpsPort)vì nó là bên chấp nhận kết nối đến trên port HTTPS server của nó. Người đăng ký không đặt port — nó học port của peer từWifiAwareNetworkInfosau khi đường dẫn dữ liệu được thiết lập.
Bắt tay hai pha (hello + ready) {#the-two-phase-handshake-hello--ready}
Phần khó nhất của Wi-Fi Aware data-path setup là định thời. Khung làm
việc yêu cầu cả hai bên gọi connectivityManager.requestNetwork trong
khoảng 500 ms của nhau — nếu một bên gọi nó trước khi bên kia đã đăng ký
yêu cầu phù hợp, khung làm việc từ chối nó ngay lập tức với
onUnavailable ("releaseRequestAsUnfulfillableByAnyFactory").
PlainApp giải quyết điều này bằng một bắt tay lớp ứng dụng hai tin nhắn
chạy trên Aware L2 message channel (cùng API sendMessage được dùng bởi
onServiceDiscovered):
Tại sao hai tin nhắn (hello + ready) thay vì chỉ một?
Hello đơn thuần là không đủ vì bất đối xứng hướng. Người đăng ký có
thể gửi hello ngay khi nó khám phá nhà xuất bản (trong onServiceDiscovered),
nhưng nhà xuất bản không thể bắt đầu requestNetwork cho đến khi có
PeerHandle của người đăng ký, thứ nó chỉ học bằng cách nhận hello. Nên
hello phục vụ hai mục đích:
- Giao PeerHandle của người đăng ký cho nhà xuất bản. Nhà xuất bản
cần nó để build
WifiAwareNetworkSpecifier. - Tín hiệu ý định kết nối. Nhận hello báo cho nhà xuất bản "người đăng ký sắp requestNetwork, nên tôi cũng nên."
Biên lai ready tồn tại cho hướng ngược lại — để báo người đăng ký
"nhà xuất bản đã đăng ký requestNetwork của nó." Nếu không có nó,
requestNetwork của người đăng ký có thể chạy trước nhà xuất bản và bị
khung làm việc từ chối. Biên lai ready là một tín hiệu không chặn:
người đăng ký không chờ nó trước khi gọi requestNetwork (điều đó sẽ
thêm một round trip), nhưng nếu nó đến trong khi người đăng ký ở trạng
thái IDLE (giữa các nỗ lực retry), người đăng ký có thể retry ngay lập
tức mà không chờ khoảng RETRY_DELAY_MS.
Sự bất đối xứng của vòng lặp retry
Đây là phần tinh tế nhất của thiết kế. Chỉ người đăng ký retry. Nhà
xuất bản thực hiện chính xác một nỗ lực requestNetwork mỗi hello nhận
được. Điều này là vì:
- Nếu cả hai bên retry độc lập, các chu kỳ retry của họ sẽ trượt lệch pha
(khác
delay()duration, khác GC pause), và hai lệnhrequestNetworkhiếm khi chồng lên trong cửa sổ 500 ms. - Vòng lặp retry của người đăng ký gửi một hello mới trên mỗi nỗ lực, thứ
kích hoạt lại
buildLinkcủa nhà xuất bản quapublishHelloListeners. Điều này đảm bảorequestNetworkcủa nhà xuất bản luôn theo sau hello ~50 ms, an toàn trong cửa sổ 500 ms.
Điều này được tài liệu hóa chi tiết trong
AwarePeerLink.build.
NDP requestNetwork — Cửa sổ 500 ms {#ndp-requestnetwork--the-500-ms-window}
Lệnh gọi requestNetwork là thao tác nhạy cảm với thời gian nhất trong
tầng vận chuyển Aware. Đây là điều xảy ra trên mỗi bên:
Callback onUnavailable nghĩa là gì
onUnavailable kích hoạt khi khung làm việc từ chối requestNetwork
trước khi tìm thấy yêu cầu peer phù hợp. Bản thân PeerHandle vẫn hợp lệ
— chỉ NDP (Neighbor Discovery Protocol) ghép cặp thất bại vì bên kia chưa
đăng ký. PlainApp cố tình không gọi session.invalidatePeerHandle
trong trường hợp này, vì invalidate handle sẽ loại bỏ tín hiệu duy nhất mà
onServiceDiscovered từng được gọi (nó kích hoạt một lần mỗi peer mỗi
vòng đời phiên subscribe). Với handle được bảo tồn, retry có thể tái sử
dụng nó thay vì chờ một khám phá mới.
Tương tự cho publisher-side handle từ onMessageReceived — nhà xuất bản
giữ entry publishPeerHandles[fromCid] qua các nỗ lực thất bại, nên
hello tiếp theo của người đăng ký tái sử dụng cached handle thay vì bị
drop.
Nhóm kết nối per-peer và quét nhàn rỗi {#per-peer-link-pool--idle-sweeping}
Mỗi peer đã pair có một đối tượng AwarePeerLink riêng, được sở hữu bởi
AwareLinkPool toàn tiến trình. Pool xử lý discovery event, link reuse,
và idle eviction.
Tại sao không auto-build khi khám phá?
Pool cố tình không build link khi onServiceDiscovered kích hoạt. Đây
là một quyết định quan trọng: một quán cà phê bận rộn có thể có 100 thiết
bị PlainApp đều publish service "plain-peer". Nếu mỗi discovery kích hoạt
một requestNetwork, khung làm việc sẽ bị tràn với các nỗ lực NDP setup
và radio Wi-Fi sẽ bị bão hòa.
Thay vào đó, pool chỉ ghi PeerHandle và chờ một trong:
- Người dùng cục bộ gửi tin nhắn →
WifiAwareTransport.send→pool.buildLink(peer)(trigger sender-side). - Peer từ xa gửi hello →
onPublishHelloReceived→buildLink(peer)(trigger receiver-side). - Peer từ xa gửi ready →
onSubscribeReadyReceived→buildLink(peer)(trigger receiver-side).
Bằng cách này, link chỉ được build cho peer mà người dùng đang thực sự trao đổi tin nhắn — không phải mọi thiết bị PlainApp trong phạm vi radio.
Quét nhàn rỗi
Mỗi 10 giây, pool duyệt tất cả link và đóng bất kỳ link nào có
lastActiveAt cũ hơn 60 giây. Mỗi send và downloadFile gọi
link.touch() để làm mới timestamp. Điều này thu hồi Wi-Fi radio context
và OkHttp connection pool cho peer mà người dùng đã ngừng chat — quan
trọng vì Android giới hạn số đường dẫn dữ liệu Aware đồng thời xuống
khoảng 4–10 (tùy thiết bị).
Định địa chỉ IPv6 và thủ thuật DNS plain-aware-peer {#ipv6-addressing--the-plain-aware-peer-dns-trick}
Đường dẫn dữ liệu Wi-Fi Aware chỉ dùng link-local IPv6 only. Không có
IPv4, không có DNS server, không có DHCP. Địa chỉ IPv6 của peer được giao
qua field WifiAwareNetworkInfo.peerIpv6Addr trong
onCapabilitiesChanged — một địa chỉ fe80::... chỉ có nghĩa trên
network interface Aware.
PlainApp cần gửi HTTPS request đến địa chỉ này, nhưng phân tích cú pháp
URL https:// của OkHttp từ chối IPv6 literal thô trong hostname
(https://[fe80::abcd]:8443/ hoạt động, nhưng route nó qua một custom
Dns resolver sạch hơn). Thủ thuật:
Tại sao sentinel hostname?
Thay thế — truyền IPv6 literal trực tiếp trong URL — sẽ yêu cầu mỗi call
site phải biết về địa chỉ link-local. Bằng cách dùng sentinel hostname,
việc xây dựng URL giống nhau cho LAN và Aware: cả hai tạo ra một URL
https://<host>:<port>/peer_graphql hợp lệ mà OkHttp có thể parse. Khác
biệt duy nhất là implementation Dns bound cho client — LAN dùng system
DNS, Aware dùng awareDns(peerIpv6) thứ trả về cached link-local address
cho sentinel hostname và fall through đến Dns.SYSTEM cho bất kỳ thứ gì
khác.
Tại sao network.socketFactory?
Đối tượng Network của Android đại diện một network interface cụ thể
(trong trường hợp này, Aware data path). Bằng cách gọi
network.socketFactory và truyền nó cho config socketFactory của
OkHttp, ta ép tất cả TCP socket được tạo trên Aware interface — không
phải default Wi-Fi hoặc cellular interface. Nếu không có điều này, OS
sẽ route request qua default network, nơi link-local IPv6 không tiếp cận
được, và request sẽ thất bại với ENETUNREACH.
Mật mã: Dẫn xuất PMK và tái sử dụng ChaCha20 {#cryptography-pmk-derivation--chacha20-reuse}
Wi-Fi Aware hỗ trợ một PMK (Pairwise Master Key) tùy chọn cho data path. Khi đặt, bản thân L2 link được mã hóa với PMK đó — Wi-Fi radio xử lý encryption, không cần application-layer crypto.
PlainApp derive PMK từ cùng shared key ChaCha20 mà LanTransport và
BleTransport dùng cho application-layer encryption:
Tại sao truncate thành 32 byte?
PMK Wi-Fi Aware phải chính xác 32 byte (256 bit). ChaCha20 shared key từ
pairing cũng là 32 byte trong trường hợp bình thường, nên nhánh
raw.size == 32 là đường dẫn phổ biến. Fallback truncation/padding xử
lý trường hợp (lý thuyết) khi key được lưu ngắn hơn — padding với zero
đến 32 byte là biện pháp phòng thủ, không phải điều xảy ra trong thực tế
với properly paired peer.
Signed envelope giống hệt với LAN
Vì createCryptoHttpClient là cùng factory được dùng bởi LanTransport,
L7 crypto trên Aware byte-for-byte identical với LAN.
PeerGraphQLService server-side không biết (hoặc quan tâm) transport nào
deliver request — nó chỉ thấy một signed, encrypted GraphQL payload và
decrypt nó với shared key của peer. Đây là nguyên tắc "one codebase, many
transport" được tài liệu hóa trong
Chat Architecture.
Đường dẫn gửi tin nhắn (End-to-End) {#message-send-path-end-to-end}
Ghép tất cả lại — điều gì xảy ra khi một tin nhắn chat được gửi qua Wi-Fi Aware:
Các lựa chọn thiết kế đáng chú ý
- Tái sử dụng kết nối. Khác với
BleTransport, tear down GATT connection sau mỗi request,WifiAwareTransporttái sử dụng Aware data path cho nhiều request như người dùng tạo trong idle window 60 s. Request đầu tiên chịu handshake ~400 ms; request kế tiếp ~10 ms round trip. - Cùng crypto với LAN. ChaCha20 interceptor và signed envelope
byte-identical với LAN.
PeerGraphQLServicecủa peer không biết transport nào deliver request. - Không preemption khi link thất bại. Nếu
buildLinkthất bại, transport throwTransportUnavailablevà router fall through sang BLE. Không có retry trongsend—AwarePeerLink.buildđã làm retry loop nội bộ riêng (vớiMAX_BUILD_ATTEMPTS = 1trên client, nhiều hơn nếu prewarmer đã prime cả hai bên).
Đường dẫn tải tệp (End-to-End) {#file-download-path-end-to-end}
Tải tệp qua Aware tái sử dụng cùng data path như tin nhắn chat, nhưng dùng một OkHttp client riêng biệt cấu hình cho streaming tệp lớn:
Tại sao client riêng biệt cho download?
Chat client (AwareHttpClientFactory.build) có requestTimeoutMillis
30 s — phù hợp cho GraphQL mutation nhưng catastrophic cho 100 MB file
download. Download client (buildFileDownload) đặt:
connectTimeoutMillis = 10_000(dài hơn 5 s của chat, tolerant hơn với slow first-packet trên fresh data path)readTimeout = 120 smỗi read (so với implicit default 10 s)requestTimeoutMillis = 120_000(2 phút — đủ cho hầu hết tệp)retryOnConnectionFailure(true)— một dropped mid-download read được retry thay vì thất bại toàn bộ transfer
Nó cũng omit ChaCha20 interceptor. Endpoint /fs serve raw file byte
(không phải signed GraphQL envelope), và L2 PMK (khi present) đã mã hóa
radio link. Double-encrypt một video 50 MB với ChaCha20 trong software sẽ
lãng phí CPU và làm chậm transfer.
Streaming, không phải buffering
Giống BLE path, Aware download stream tệp qua một ByteReadChannel — tệp
được ghi vào temp file khi byte đến, không buffer trong memory.
PeerFileDownloader đọc 8 KB chunk và emit progress event mỗi giây. Cùng
pipeline DownloadedResponse / PeerFileDownloader / DownloadQueue
được tái sử dụng qua tất cả transport — transport-specific chỉ channel
source.
Làm nóng trước: Khởi động Aware được kích hoạt qua BLE {#prewarming-ble-triggered-aware-startup}
Độ trễ user-visible lớn nhất trong Aware path là first handshake — nếu cả hai bên chưa bắt đầu Aware, tin nhắn đầu tiên của người dùng phải chờ:
- Local Aware session attach (~1 s)
- Local publish + subscribe start (~1 s)
- Aware startup của remote peer (~2 s qua BLE)
- Mutual discovery (~1 s)
- NDP handshake (~400 ms)
Đó là ~5 giây trước khi byte đầu tiên được gửi. Để ẩn latency này,
PeerTransportPrewarmer chạy khi vào ChatPage và kích hoạt Aware
startup của remote peer qua BLE:
Vai trò kép của BLE
BLE phục vụ hai mục đích ở đây:
- Đọc Aware state hiện tại của peer (rẻ, không GATT connect —
serviceDatabyte0 của scan response mang Aware flag). - Kích hoạt peer bắt đầu Aware nếu nó hỗ trợ nhưng hiện không chạy.
Điều này đi qua regular
BleTransport.sendpath — mộtstartAwareGraphQL mutation mã hóa với shared key ChaCha20, giao qua GATT RPC đến endpoint/peer_graphqlcủa peer.
Đây là một trong số ít nơi transport hợp tác thay vì chỉ fall back: BLE được dùng để pre-emptively upgrade phiên sang transport Aware nhanh hơn, trước khi người dùng kể cả nhận ra.
Tại sao optimistic setAwareRunning(true)?
Mutation startAware trả về success ngay khi resolver của remote peer
gọi WifiAwareTransport.start() — nhưng Aware session chưa thực sự attach
(onAttached kích hoạt bất đồng bộ). PlainApp mark peer là
awareRunning = true optimistic, vì:
- Nếu nó thực sự start,
sendkế tiếp sẽ dùng Aware (nhanh). - Nếu không (ví dụ Wi-Fi của peer off),
buildLinkcủasendkế tiếp sẽ thất bại vớiTransportUnavailablevà fall back sang BLE tự nhiên. - Cost của false positive là một timeout ~5 s, không phải block vĩnh viễn
—
PeerCircuitBreakerghi nhận failure nhưng không mở BLE leg (BLE chỉ mở trên failure riêng của nó).
Tại sao throttle 30 s?
PeerTransportPrewarmer.prewarm(peerId) ghi timestamp mỗi peer và từ chối
re-run trong 30 s. Điều này là vì người dùng điều hướng lui tới giữa chat
list và chat page thường xuyên — không có throttle, mỗi điều hướng sẽ
kích hoạt một BLE scan + startAware mutation, drain pin và spam BLE radio.
Cửa sổ 30 s đủ ngắn để catch một peer vừa online (ví dụ người dùng mở app
trên thiết bị từ xa) nhưng đủ dài để tránh spurious re-run.
Chế độ lỗi và cờ hiệu bỏ qua nhanh {#failure-modes--the-fast-skip-flag}
Aware có nhiều chế độ lỗi hơn bất kỳ transport nào khác. Cờ hiệu bỏ qua
nhanh isAwareRunning là tối ưu hóa quan trọng nhất trong toàn bộ module
— nếu không có nó, mỗi send sẽ lãng phí 10 s trên buildLink timeout
trước khi fall back sang BLE.
Cờ hiệu isAwareRunning là then chốt
Nếu không có boolean duy nhất này, mỗi send Aware sẽ hoặc:
- Luôn nỗ lực
buildLink→ 10 s timeout trên mỗi send đến peer mà Aware không chạy. - Luôn skip Aware → không bao giờ dùng nó kể cả khi cả hai bên đều chạy.
Cờ hiệu được làm mới từ hai nguồn, theo thứ tự authority:
- BLE scan response (rẻ, không GATT connect) — đặt bởi
PeerTransportPrewarmer.refreshAwareFlagFromScan. Peer advertise Aware state của nó trong payloadserviceData9-byte (byte0 bitfield). - GATT DISCOVER reply (authoritative) — đặt bởi
PairingTransport.scanAndDiscoverkhi một full discovery xảy ra. Điều này overwrite scan hint.
Khi false, WifiAwareTransport.send và downloadFile throw
TransportUnavailable ngay lập tức — không scan, không handshake,
không timeout. Router fall through sang BLE trong micro giây.
Tham chiếu hằng số chính {#key-constants-reference}
| Constant | Value | Where | Purpose |
|---|---|---|---|
AwareSession.SERVICE_NAME | "plain-peer" | Khám phá | Tên service được publish & subscribe bởi mọi thiết bị PlainApp |
AwareSession.PEER_HANDLE_MAX_AGE_MS | 30 000 | PeerHandle cache | Loại bỏ handle cũ (phiên publish của peer có thể đã được khởi động lại) |
AwareSession.READY_TIMEOUT_MS | 15 000 | Bắt tay | Người đăng ký chờ biên lai ready của nhà xuất bản |
AwareSession.MSG_HELLO | 0 | Bắt tay | ID tin nhắn Người đăng ký → Nhà xuất bản |
AwareSession.MSG_READY | 1 | Bắt tay | ID tin nhắn Nhà xuất bản → Người đăng ký |
AwarePeerLink.MAX_BUILD_ATTEMPTS | 1 | Bắt tay (chỉ client) | Một nỗ lực — từng là 3, giờ là 1 vì prewarmer đã chuẩn bị cả hai bên |
AwarePeerLink.ATTEMPT_TIMEOUT_MS | 5 000 | Bắt tay | Timeout mỗi nỗ lực — từng là 10 s, giảm một nửa để tăng tốc fallback |
AwarePeerLink.RETRY_DELAY_MS | 500 | Bắt tay | Độ trễ giữa các lần retry (chỉ client) |
AwarePeerLink.REQUEST_TIMEOUT_MS | 30 000 | NDP | Timeout connectivityManager.requestNetwork |
AwareLinkPool.IDLE_TIMEOUT_MS | 60 000 | Quét pool | Đóng link nhàn rỗi sau 60 s không hoạt động |
AwareLinkPool.IDLE_SWEEP_INTERVAL_MS | 10 000 | Quét pool | Khoảng thời gian quét |
AwareHttpClientFactory.AWARE_HOST | "plain-aware-peer" | DNS | Sentinel hostname được resolve thành IPv6 peer bởi Dns tùy chỉnh |
build chat client | connectTimeout 5 s, requestTimeout 30 s, ChaCha20 interceptor | ||
buildFileDownload | connectTimeout 10 s, readTimeout 120 s, requestTimeout 120 s, không crypto | ||
PeerTransportPrewarmer.PREWARM_TTL_MS | 30 000 | Prewarm | Throttle mỗi peer |
PeerTransportPrewarmer.BLE_SCAN_TIMEOUT_MS | 15 000 | Prewarm | BLE scan timeout cho refreshAwareFlagFromScan |
PeerCircuitBreaker.WINDOW_MS | 30 000 | Circuit breaker | Thời gian mở sau ngưỡng |
PeerCircuitBreaker.MAX_FAILURES | 2 | Circuit breaker | Số failure trong window để mở |
TempData.httpsPort | 8443 (default) | Server | Port của nhà xuất bản được quảng bá qua WifiAwareNetworkSpecifier.setPort |
BleServiceData.AWARE_SUPPORTED | 0x01 | BLE scan response | Bit chỉ peer hỗ trợ Wi-Fi Aware |
BleServiceData.AWARE_RUNNING | 0x02 | BLE scan response | Bit chỉ service Aware của peer hiện đang chạy |
Tóm tắt đánh đổi thiết kế {#design-trade-offs-recap}
Đọc thêm {#further-reading}
- Chat Architecture — cách
WifiAwareTransportphù hợp với chuỗi dự phòngLAN → Aware → BLEvà broader chat send/receive pipeline. - BLE Transport — last-resort transport tiếp quản khi Aware không khả dụng; cũng là channel được dùng bởi prewarmer để kích hoạt Aware startup trên remote peer.
- Pairing Flow — cách shared key ChaCha20 được tái
sử dụng làm Aware PMK được thiết lập, và cách BLE scan response flag
(
AWARE_SUPPORTED/AWARE_RUNNING) được populate.