O artigo cobre o ciclo de vida da sessão Aware (apenas Android), o modelo de
descoberta publish / subscribe, o handshake de duas fases com divisão de
papéis que sincroniza o requestNetwork em ambos os lados dentro da janela
de ~500 ms do framework, o pool de links por par com limpeza de ociosos, o
truque do IPv6 + DNS customizado que permite a um único cliente OkHttp servir
tanto LAN quanto Aware, e o prewarmer que dispara a inicialização do Aware do
par via BLE.
Para a cadeia de fallback mais ampla, consulte Arquitetura de Chat. Para o transporte BLE que assume quando o Aware está indisponível, consulte Transporte BLE. Para saber como a chave ChaCha20 compartilhada reutilizada como PMK do Aware é estabelecida, consulte Fluxo de Pareamento.
Sumário
- Por que Wi-Fi Aware?
- Onde o Aware Fica na Cadeia de Fallback
- Ciclo de Vida da Sessão: Attach → Publish + Subscribe
- Descoberta e Atribuição de Papéis
- O Handshake de Duas Fases (hello + ready)
- NDP requestNetwork — A Janela de 500 ms
- Pool de Links por Par e Limpeza de Ociosos
- Endereçamento IPv6 e o Truque de DNS
plain-aware-peer - Criptografia: Derivação de PMK e Reuso de ChaCha20
- Caminho de Envio de Mensagens (Ponta a Ponta)
- Caminho de Download de Arquivos (Ponta a Ponta)
- Prewarming: Inicialização do Aware Disparada por BLE
- Modos de Falha e a Flag de Skip Rápido
- Referência de Constantes-Chave
- Recapitulação das Trocas de Design
Por que Wi-Fi Aware? {#why-wi-fi-aware}
O Wi-Fi Aware (IEEE 802.11bc, antigamente NAN — Neighbor Awareness Networking) é uma certificação da Wi-Fi Alliance que permite a dois dispositivos se descobrirem e trocar dados sem qualquer infraestrutura Wi-Fi — sem AP, sem router, sem DHCP. O PlainApp o usa em dois cenários que a LAN não consegue cobrir:
- SSIDs / VLANs diferentes. Um telefone na rede de visitantes e um laptop na VLAN IoT estão ambos "online" via Wi-Fi, mas não conseguem alcançar o IP um do outro. O Aware cria um caminho de dados direto dispositivo-a-dispositivo que ignora completamente a infraestrutura.
- Sem infraestrutura alguma. Dois dispositivos no meio do nada com o Wi-Fi ligado, mas sem AP, ainda assim conseguem conversar. (O BLE também cobre isso, mas o Aware é muito mais rápido — ~10 ms de ida e volta vs segundos, e MB/s vs dezenas de KB/s.)
Restrições de plataforma
O Wi-Fi Aware é apenas Android no PlainApp:
- Android 13 (API 33) é o mínimo — as sobrecargas de
WifiAwareNetworkSpecifier.BuildercomsetPort()esetPmk()das quais o PlainApp depende requeremisTPlus(). - O iOS não expõe o Wi-Fi Aware para apps de terceiros. O PlainApp no iOS cai
diretamente da LAN para o BLE; o objeto
WifiAwareTransportnem é compilado para o target iOS (@RequiresApi(Build.VERSION_CODES.S)+ source set androidMain).
É por isso que o PeerTransportRouter.buildList chama
createWifiAwareTransport() — uma fábrica que retorna null no iOS.
Onde o Aware Fica na Cadeia de Fallback {#where-aware-sits-in-the-fallback-chain}
O PeerTransportRouter do PlainApp é uma lista ordenada. Para cada chamada de
send ou downloadFile, ele percorre a lista e tenta cada transporte até um
deles ter sucesso; falhas cascateiam para baixo.
Por que o Aware é "o meio" e não "o primeiro"?
Porque a LAN é quase sempre mais rápida quando disponível. Um salto Wi-Fi na mesma sub-rede por um AP é uma única troca de quadro 802.11; um data path do Aware adiciona um setup de NDP (~5 s no primeiro uso) mais um segundo contexto de rádio Wi-Fi para o link dispositivo-a-dispositivo. Se ambos são alcançáveis, a LAN vence em latência e vazão.
Por outro lado, o BLE é sempre mais lento — mas funciona sempre que ambos os dispositivos estão pareados. O Aware fica no meio: mais rápido que o BLE, mais lento que a LAN, e disponível apenas em dispositivos Android 13+ com o Wi-Fi ligado.
Ciclo de Vida da Sessão: Attach → Publish + Subscribe {#session-lifecycle-attach--publish--subscribe}
Uma sessão Wi-Fi Aware é de processo inteiro. Há exatamente um
WifiAwareSession por dispositivo; dentro dela, o PlainApp roda uma sessão
de publish (para os pares nos descobrirem) e uma sessão de subscribe
(para descobrirmos os pares). Ambas são iniciadas no momento em que
AwareSession.start() completa o callback de attach.
Por que publicar E subscrever no mesmo dispositivo?
O modelo de descoberta do Wi-Fi Aware é assimétrico: um publisher anuncia
um serviço, um subscriber procura por ele. Para tornar a descoberta simétrica
(ambos os dispositivos se descobrem), o PlainApp faz ambos ao mesmo tempo.
Sem isso, o dispositivo A teria que saber de antemão se é o publisher ou o
subscriber para um dado par — mas os papéis dos pares são determinados depois
pela comparação de clientId (consulte
Descoberta e Atribuição de Papéis).
Publicar e subscrever simultaneamente significa que cada dispositivo vê o
onServiceDiscovered do outro (como subscriber) E recebe as mensagens hello
do outro (como publisher) — ambas as direções do handshake estão sempre
disponíveis.
Reinício automático no término
Algumas variantes do Android (MIUI em particular) matam sessões Aware de longa
duração para economizar bateria. O PlainApp trata isso nos callbacks de
onSessionTerminated: ele anula a sessão terminada e chama imediatamente
publishOwnService / subscribeOwnService de novo na WifiAwareSession que
ainda está attached. A sessão de attach em si não é perdida — apenas a sessão
de descoberta publish/subscribe. Peer handles de antes do término ficam
obsoletos, por isso o awaitPeerHandle verifica o timestamp de
discoveredAt e descarta handles mais velhos que 30 s.
Descoberta e Atribuição de Papéis {#discovery--role-assignment}
O protocolo de data path do Wi-Fi Aware exige que um lado atue como publisher (servidor) e o outro como subscriber (cliente). Ambos os lados não podem ser, simultaneamente, o initiator — o framework rejeita requisições sem uma contraparte correspondente.
O PlainApp atribui papéis deterministicamente por par usando uma simples comparação lexicográfica de clientIds:
Por que determinístico e não negociado?
Uma abordagem negociada (por exemplo, "MAC menor é o servidor") exigiria uma
troca de mensagens extra. A comparação lexicográfica é idempotente,
simétrica e stateless: ambos os dispositivos computam o mesmo papel para o
mesmo par sem qualquer comunicação. O clientId é um UUID curto de 13
caracteres, então empates (clientId == peer.id) só acontecem ao comparar um
par com ele mesmo — o que nunca chega ao transporte.
O papel determina duas coisas downstream:
- Quem conduz o loop de retry. Apenas o cliente retenta o
requestNetwork; o servidor faz exatamente uma tentativa por hello recebido. Isso é crítico para a janela de 500 ms (próxima seção). - Quem define a porta. O publisher chama
setPort(httpsPort)porque é ele quem aceita conexões de entrada na porta do seu servidor HTTPS. O subscriber não define porta — ele aprende a porta do par a partir doWifiAwareNetworkInfodepois que o data path é estabelecido.
O Handshake de Duas Fases (hello + ready) {#the-two-phase-handshake-hello--ready}
A parte mais difícil do setup de data path do Wi-Fi Aware é o timing. O
framework exige que ambos os lados chamem connectivityManager.requestNetwork
dentro de aproximadamente 500 ms um do outro — se um lado chamar antes de o
outro ter registrado sua requisição correspondente, o framework a rejeita
imediatamente com onUnavailable
("releaseRequestAsUnfulfillableByAnyFactory").
O PlainApp resolve isso com um handshake de duas mensagens em camada de
aplicação que roda sobre o canal de mensagens L2 do Aware (a mesma API
sendMessage usada por onServiceDiscovered):
Por que duas mensagens (hello + ready) em vez de apenas uma?
O hello sozinho não basta por causa da assimetria de direção. O subscriber
pode enviar o hello no instante em que descobre o publisher (em
onServiceDiscovered), mas o publisher não consegue iniciar o
requestNetwork até ter o PeerHandle do subscriber, que só aprende ao
receber o hello. Então o hello serve a dois propósitos:
- Entregar o PeerHandle do subscriber ao publisher. O publisher precisa
dele para construir o
WifiAwareNetworkSpecifier. - Sinalizar intenção de conectar. Receber o hello diz ao publisher "o subscriber está prestes a fazer requestNetwork, então eu deveria também."
O recibo ready existe para a direção oposta — para dizer ao subscriber
"o publisher registrou seu requestNetwork." Sem ele, o requestNetwork do
subscriber poderia correr à frente do publisher e ser rejeitado pelo
framework. O recibo ready é um sinal não bloqueante: o subscriber não
espera por ele antes de chamar requestNetwork (isso adicionaria uma ida e
volta), mas se ele chegar enquanto o subscriber está no estado IDLE (entre
tentativas de retry), o subscriber pode repetir imediatamente sem esperar o
intervalo de RETRY_DELAY_MS.
A assimetria do loop de retry
Essa é a parte mais sutil do design. Apenas o subscriber retenta. O
publisher faz exatamente uma tentativa de requestNetwork por hello recebido.
Isso porque:
- Se ambos os lados retentassem independentemente, seus ciclos de retry
sairiam de fase (durações diferentes de
delay(), pausas diferentes de GC), e as duas chamadas derequestNetworkraramente se sobrepõoriam dentro da janela de 500 ms. - O loop de retry do subscriber envia um hello novo a cada tentativa, o que
reativa o
buildLinkdo publisher viapublishHelloListeners. Isso garante que orequestNetworkdo publisher sempre siga o hello em ~50 ms, bem dentro da janela de 500 ms.
Isso está documentado em detalhes em
AwarePeerLink.build.
NDP requestNetwork — A Janela de 500 ms {#ndp-requestnetwork--the-500-ms-window}
A chamada requestNetwork é a operação mais sensível a timing no transporte
Aware. Veja o que acontece em cada lado:
O que o callback onUnavailable significa
onUnavailable dispara quando o framework rejeita o requestNetwork antes de
encontrar uma requisição de par correspondente. O PeerHandle em si ainda é
válido — apenas o pareamento NDP (Neighbor Discovery Protocol) falhou porque
o outro lado ainda não tinha registrado. O PlainApp deliberadamente não
chama session.invalidatePeerHandle nesse caso, porque invalidar o handle
descartaria o único sinal de que onServiceDiscovered foi chamado (ele dispara
uma vez por par por tempo de vida da sessão de subscribe). Com o handle
preservado, o retry pode reutilizá-lo em vez de esperar por uma descoberta
nova.
O mesmo vale para o handle do lado do publisher vindo de onMessageReceived —
o publisher mantém a entrada publishPeerHandles[fromCid] entre tentativas
falhadas, de modo que o próximo hello do subscriber reutiliza o handle em
cache em vez de ser descartado.
Pool de Links por Par e Limpeza de Ociosos {#per-peer-link-pool--idle-sweeping}
Cada par pareado tem seu próprio objeto AwarePeerLink, pertencente ao
AwareLinkPool de processo inteiro. O pool trata eventos de descoberta, reuso
de link e expulsão de ociosos.
Por que não auto-build na descoberta?
O pool explicitamente não constrói um link quando onServiceDiscovered
dispara. Essa é uma decisão crítica: uma cafeteria movimentada pode ter 100
dispositivos PlainApp publicando o serviço "plain-peer". Se cada descoberta
disparasse um requestNetwork, o framework seria inundado de tentativas de
setup NDP e o rádio Wi-Fi seria saturado.
Em vez disso, o pool apenas registra o PeerHandle e espera por um de:
- O usuário local envia uma mensagem →
WifiAwareTransport.send→pool.buildLink(peer)(gatilho do lado remetente). - O par remoto envia um hello →
onPublishHelloReceived→buildLink(peer)(gatilho do lado receptor). - O par remoto envia um ready →
onSubscribeReadyReceived→buildLink(peer)(gatilho do lado receptor).
Assim, links são construídos apenas para pares com os quais o usuário está efetivamente trocando mensagens — não para todo dispositivo PlainApp no alcance do rádio.
Limpeza de ociosos
A cada 10 segundos, o pool percorre todos os links e fecha qualquer um cujo
lastActiveAt seja mais velho que 60 segundos. Cada send e downloadFile
chama link.touch() para atualizar o timestamp. Isso recupera o contexto de
rádio Wi-Fi e o pool de conexões OkHttp para pares com os quais o usuário
parou de conversar — importante porque o Android limita o número de data paths
Aware simultâneos a aproximadamente 4–10 (depende do dispositivo).
Endereçamento IPv6 e o Truque de DNS plain-aware-peer {#ipv6-addressing--the-plain-aware-peer-dns-trick}
Data paths do Wi-Fi Aware usam apenas IPv6 link-local. Não há IPv4, nem
servidor DNS, nem DHCP. O endereço IPv6 do par é entregue via o campo
WifiAwareNetworkInfo.peerIpv6Addr em onCapabilitiesChanged — um endereço
fe80::... que só é significativo na interface de rede do Aware.
O PlainApp precisa enviar requisições HTTPS para esse endereço, mas o parsing
de URLs https:// do OkHttp recusa literais IPv6 brutos como hostname
(https://[fe80::abcd]:8443/ funciona, mas roteá-lo por um resolver de Dns
customizado é mais limpo). O truque:
Por que um hostname sentinela?
A alternativa — passar o literal IPv6 direto na URL — exigiria que todo ponto
de chamada soubesse do endereço link-local. Ao usar um hostname sentinela, a
construção da URL é idêntica para LAN e Aware: ambas produzem uma URL
https://<host>:<port>/peer_graphql válida que o OkHttp consegue fazer parse.
A única diferença é a implementação de Dns vinculada ao cliente — a LAN usa
o DNS do sistema, o Aware usa awareDns(peerIpv6) que retorna o endereço
link-local em cache para o hostname sentinela e cai para Dns.SYSTEM para
qualquer outra coisa.
Por que network.socketFactory?
O objeto Network do Android representa uma interface de rede específica
(neste caso, o data path do Aware). Ao chamar network.socketFactory e passá-lo
para a configuração socketFactory do OkHttp, forçamos todos os sockets TCP a
serem criados na interface do Aware — não na interface Wi-Fi ou celular
padrão. Sem isso, o SO rotearia a requisição pela rede padrão, onde o IPv6
link-local é inalcançável, e a requisição falharia com ENETUNREACH.
Criptografia: Derivação de PMK e Reuso de ChaCha20 {#cryptography-pmk-derivation--chacha20-reuse}
O Wi-Fi Aware suporta um PMK (Pairwise Master Key) opcional para o data path. Quando definido, o link L2 em si é criptografado com aquele PMK — o rádio Wi-Fi trata da criptografia, sem crypto em camada de aplicação.
O PlainApp deriva o PMK da mesma chave compartilhada ChaCha20 que
LanTransport e BleTransport usam para criptografia em camada de aplicação:
Por que truncar para 32 bytes?
O PMK do Wi-Fi Aware deve ser exatamente 32 bytes (256 bits). A chave
ChaCha20 compartilhada do pareamento também é 32 bytes no caso normal, então
o branch raw.size == 32 é o caminho comum. O fallback de truncamento/padding
trata o caso (teórico) em que a chave foi armazenada mais curta — preencher
com zeros até 32 bytes é uma medida defensiva, não algo que acontece na
prática com pares devidamente pareados.
O envelope assinado é idêntico ao da LAN
Como createCryptoHttpClient é a mesma fábrica usada por LanTransport, a
crypto L7 no Aware é byte a byte idêntica à LAN. O PeerGraphQLService do
lado servidor não sabe (nem se importa) qual transporte entregou a requisição
— ele apenas vê um payload GraphQL assinado e criptografado e o descriptografa
com a chave compartilhada do par. Esse é o princípio "uma base de código,
muitos transportes" documentado em
Arquitetura de Chat.
Caminho de Envio de Mensagens (Ponta a Ponta) {#message-send-path-end-to-end}
Juntando tudo — o que acontece quando uma mensagem de chat é enviada via Wi-Fi Aware:
Escolhas de design notáveis
- Reuso de conexão. Ao contrário do
BleTransport, que encerra a conexão GATT após cada requisição, oWifiAwareTransportreutiliza o data path do Aware para tantas requisições quanto o usuário fizer dentro da janela de ociosidade de 60 s. A primeira requisição paga o handshake de ~400 ms; requisições subsequentes são ~10 ms de ida e volta. - Mesma crypto da LAN. O interceptador ChaCha20 e o envelope assinado são
byte a byte idênticos à LAN. O
PeerGraphQLServicedo par não sabe qual transporte entregou a requisição. - Sem preempção em falha de link. Se
buildLinkfalhar, o transporte lançaTransportUnavailablee o router cai para o BLE. Não há retry dentro desend—AwarePeerLink.buildjá faz seu próprio loop interno de retry (comMAX_BUILD_ATTEMPTS = 1no cliente, mais se o prewarmer tiver preparado ambos os lados).
Caminho de Download de Arquivos (Ponta a Ponta) {#file-download-path-end-to-end}
Downloads de arquivos via Aware reutilizam o mesmo data path das mensagens de chat, mas usam um cliente OkHttp separado configurado para streaming de arquivos grandes:
Por que um cliente separado para downloads?
O cliente de chat (AwareHttpClientFactory.build) tem um
requestTimeoutMillis de 30 s — apropriado para mutações GraphQL, mas
catastrófico para um download de arquivo de 100 MB. O cliente de download
(buildFileDownload) define:
connectTimeoutMillis = 10_000(maior que os 5 s do chat, mais tolerante a um primeiro pacote lento num data path novo)readTimeout = 120 spor leitura (vs o padrão implícito de 10 s)requestTimeoutMillis = 120_000(2 minutos — suficiente para a maioria dos arquivos)retryOnConnectionFailure(true)— uma leitura interrompida no meio do download é repetida em vez de falhar a transferência inteira
Ele também omite o interceptador ChaCha20. O endpoint /fs serve bytes
de arquivo brutos (não um envelope GraphQL assinado), e o PMK L2 (quando
presente) já criptografa o link de rádio. Criptografar duas vezes um vídeo de
50 MB com ChaCha20 em software desperdiçaria CPU e lentificaria a transferência.
Streaming, sem bufferização
Como no caminho BLE, downloads Aware fazem streaming do arquivo por um
ByteReadChannel — o arquivo é gravado em um arquivo temporário conforme os
bytes chegam, não bufferizado em memória. O PeerFileDownloader lê chunks de
8 KB e emite eventos de progresso a cada segundo. O mesmo pipeline
DownloadedResponse / PeerFileDownloader / DownloadQueue é reutilizado
entre todos os transportes — específico do transporte é apenas a fonte do
channel.
Prewarming: Inicialização do Aware Disparada por BLE {#prewarming-ble-triggered-aware-startup}
A maior latência visível ao usuário no caminho Aware é o primeiro handshake — se ambos os lados ainda não iniciaram o Aware, a primeira mensagem do usuário precisa esperar por:
- Attach da sessão Aware local (~1 s)
- Início do publish + subscribe local (~1 s)
- Inicialização do Aware no par remoto (~2 s via BLE)
- Descoberta mútua (~1 s)
- Handshake NDP (~400 ms)
São ~5 segundos antes de o primeiro byte ser enviado. Para esconder essa
latência, o PeerTransportPrewarmer roda na entrada do ChatPage e dispara
a inicialização do Aware do par remoto via BLE:
O papel duplo do BLE
O BLE tem dois papéis aqui:
- Ler o estado atual do Aware do par (barato, sem GATT connect — o
serviceDatada scan response no byte0 carrega as flags do Aware). - Disparar o par para iniciar o Aware, se ele suporta mas não está
rodando no momento. Isso passa pelo caminho regular de
BleTransport.send— uma mutação GraphQLstartAwarecriptografada com a chave ChaCha20 compartilhada, entregue via RPC GATT ao endpoint/peer_graphqldo par.
Este é um dos poucos lugares em que os transportes cooperam em vez de apenas cair para o próximo: o BLE é usado para preemptivamente atualizar a sessão para o transporte Aware mais rápido, antes mesmo de o usuário perceber.
Por que setAwareRunning(true) otimista?
A mutação startAware retorna sucesso assim que o resolver do par remoto
invoca WifiAwareTransport.start() — mas a sessão Aware ainda não está
attached de fato (onAttached dispara assincronamente). O PlainApp marca o
par como awareRunning = true otimisticamente, porque:
- Se ela realmente iniciou, o próximo
sendusará o Aware (rápido). - Se não (por exemplo, o Wi-Fi do par está desligado), o
buildLinkdo próximosendfalhará comTransportUnavailablee cairá para o BLE naturalmente. - O custo de um falso positivo é um timeout de ~5 s, não um bloqueio
permanente — o
PeerCircuitBreakerregistra a falha, mas não abre a perna do BLE (o BLE só abre com suas próprias falhas).
Por que throttling para 30 s?
PeerTransportPrewarmer.prewarm(peerId) registra um timestamp por par e se
recusa a re-executar dentro de 30 s. Isso é porque o usuário navega para frente
e para trás entre a lista de chats e a página de chat com frequência — sem
throttling, toda navegação dispararia um scan BLE + mutação startAware,
drenando bateria e bombardeando o rádio BLE. A janela de 30 s é curta o
suficiente para capturar um par que acabou de ficar online (por exemplo, o
usuário abriu o app no dispositivo remoto), mas longa o suficiente para evitar
re-execuções espúrias.
Modos de Falha e a Flag de Skip Rápido {#failure-modes--the-fast-skip-flag}
O Aware tem mais modos de falha do que qualquer outro transporte. A flag de
skip rápido isAwareRunning é a otimização mais importante de todo o módulo
— sem ela, todo send desperdiçaria 10 s em buildLink dando timeout antes
de cair para o BLE.
A flag isAwareRunning é o pilar
Sem esse único booleano, todo send do Aware ou:
- Sempre tentaria
buildLink→ 10 s de timeout em todo send para um par cujo Aware não está rodando. - Sempre pularia o Aware → nunca o usaria, mesmo quando ambos os lados o têm rodando.
A flag é atualizada a partir de duas fontes, em ordem de autoridade:
- Scan response do BLE (barato, sem GATT connect) — definida por
PeerTransportPrewarmer.refreshAwareFlagFromScan. O par anuncia seu estado Aware no payloadserviceDatade 9 bytes (bitfield no byte0). - Resposta DISCOVER via GATT (autoritativa) — definida por
PairingTransport.scanAndDiscoverquando uma descoberta completa acontece. Isso sobrescreve a dica de scan.
Quando falsa, WifiAwareTransport.send e downloadFile lançam
TransportUnavailable imediatamente — sem scan, sem handshake, sem
timeout. O router cai para o BLE em microssegundos.
Referência de Constantes-Chave {#key-constants-reference}
| Constante | Valor | Onde | Propósito |
|---|---|---|---|
AwareSession.SERVICE_NAME | "plain-peer" | Descoberta | Nome do serviço publicado e subscrito por todo dispositivo PlainApp |
AwareSession.PEER_HANDLE_MAX_AGE_MS | 30 000 | Cache de PeerHandle | Descarta handles obsoletos (a sessão de publish do par pode ter reiniciado) |
AwareSession.READY_TIMEOUT_MS | 15 000 | Handshake | Espera do subscriber pelo recibo ready do publisher |
AwareSession.MSG_HELLO | 0 | Handshake | ID de mensagem Subscriber → Publisher |
AwareSession.MSG_READY | 1 | Handshake | ID de mensagem Publisher → Subscriber |
AwarePeerLink.MAX_BUILD_ATTEMPTS | 1 | Handshake (apenas cliente) | Uma única tentativa — era 3, agora 1 porque o prewarmer prepara ambos os lados |
AwarePeerLink.ATTEMPT_TIMEOUT_MS | 5 000 | Handshake | Timeout por tentativa — era 10 s, cortado pela metade para acelerar o fallback |
AwarePeerLink.RETRY_DELAY_MS | 500 | Handshake | Atraso entre tentativas de retry (apenas cliente) |
AwarePeerLink.REQUEST_TIMEOUT_MS | 30 000 | NDP | Timeout de connectivityManager.requestNetwork |
AwareLinkPool.IDLE_TIMEOUT_MS | 60 000 | Limpeza do pool | Fecha links ociosos após 60 s de inatividade |
AwareLinkPool.IDLE_SWEEP_INTERVAL_MS | 10 000 | Limpeza do pool | Intervalo da varredura |
AwareHttpClientFactory.AWARE_HOST | "plain-aware-peer" | DNS | Hostname sentinela resolvido para o IPv6 do par por Dns customizado |
build chat client | connectTimeout 5 s, requestTimeout 30 s, interceptador ChaCha20 | ||
buildFileDownload | connectTimeout 10 s, readTimeout 120 s, requestTimeout 120 s, sem crypto | ||
PeerTransportPrewarmer.PREWARM_TTL_MS | 30 000 | Prewarm | Throttle por par |
PeerTransportPrewarmer.BLE_SCAN_TIMEOUT_MS | 15 000 | Prewarm | Timeout de scan BLE para refreshAwareFlagFromScan |
PeerCircuitBreaker.WINDOW_MS | 30 000 | Circuit breaker | Duração aberto após threshold |
PeerCircuitBreaker.MAX_FAILURES | 2 | Circuit breaker | Falhas dentro da janela para abrir |
TempData.httpsPort | 8443 (padrão) | Servidor | Porta do publisher anunciada via WifiAwareNetworkSpecifier.setPort |
BleServiceData.AWARE_SUPPORTED | 0x01 | Scan response do BLE | Bit indicando que o par suporta Wi-Fi Aware |
BleServiceData.AWARE_RUNNING | 0x02 | Scan response do BLE | Bit indicando que o serviço Aware do par está rodando no momento |
Recapitulação das Trocas de Design {#design-trade-offs-recap}
Leitura Adicional
- Arquitetura de Chat — como o
WifiAwareTransportse encaixa na cadeia de fallbackLAN → Aware → BLEe no pipeline mais amplo de envio/recebimento de chat. - Transporte BLE — o transporte de último recurso que assume quando o Aware está indisponível; também é o canal usado pelo prewarmer para disparar a inicialização do Aware no par remoto.
- Fluxo de Pareamento — como a chave ChaCha20
compartilhada reutilizada como PMK do Aware é estabelecida, e como as flags
da scan response do BLE (
AWARE_SUPPORTED/AWARE_RUNNING) são preenchidas.