ブログに戻る
Transport19 min read

Wi-Fi Aware トランスポート設計 — 近隣ディスカバリとデータパス

本記事では、PlainApp がピアトランスポートのフォールバックチェーンの中間層として Wi-Fi Aware (NAN — Neighbor Awareness Networking) をどう使うかを説明します。LAN (同じサブネットの HTTPS) と BLE (最後の手段の GATT RPC) の間に位置します。Wi-Fi Aware は、2 台の PlainApp デバイスが異なる SSID、ゲスト vs IoT VLAN、あるいは Wi-Fi インフラストラクチャが全くない状況でも通信できるようにするもので — DHCP サーバーからの IP アドレスを一切必要としません。

本記事は Android のみの Aware セッションライフサイクル、publish/subscribe ディスカバリモデル、フレームワークの ~500 ms 窓内で両側の requestNetwork を同期させる 2 フェーズロール分割ハンドシェイク、アイドルスイープ付き per-peer リンプール、単一の OkHttp クライアントで LAN と Aware の両方をまかなう IPv6 + カスタム DNS のトリック、そして BLE 経由でピアの Aware 起動をトリガーする prewarmer を扱います。

広範なフォールバックチェーンについては Chat Architecture を参照してください。 Aware が利用不可のときに引き継ぐ BLE トランスポートについては BLE Transport を参照してください。Aware PMK として再利用される共通 ChaCha20 鍵がどう確立されるかについては Pairing Flow を参照してください。

目次

なぜ Wi-Fi Aware なのか {#why-wi-fi-aware}

Wi-Fi Aware (IEEE 802.11bc、旧称 NAN — Neighbor Awareness Networking) は、Wi-Fi Alliance の認証で、2 台のデバイスが Wi-Fi インフラストラクチャなしで互いを発見しデータを交換 できるようにします — AP もルーターも DHCP も不要です。PlainApp は LAN ではカバーできない 2 つのシナリオでこれを使います:

  • 異なる SSID / VLAN。 ゲストネットワークのスマートフォンと IoT VLAN のラップトップはどちらも Wi-Fi 経由で「オンライン」ですが、お互いの IP に到達できません。Aware はインフラストラクチャを完全にバイパスするデバイス間直接データパスを作ります。
  • インフラストラクチャが全くない。 Wi-Fi がオンだが AP がない場所にいる 2 台のデバイスでもチャットできます。(BLE もこれをカバーしますが、Aware の方がはるかに高速です — 数秒 vs ~10 ms ラウンドトリップ、数十 KB/s vs MB/s。)

Diagram 1
1

プラットフォームの制約

Wi-Fi Aware は PlainApp では Android のみ です:

  • Android 13 (API 33) が最小です — PlainApp が依存する setPort()setPmk() オーバーロード付きの WifiAwareNetworkSpecifier.BuilderisTPlus() を要求します。
  • iOS はサードパーティアプリに Wi-Fi Aware を公開していません。iOS の PlainApp は LAN から直接 BLE にフォールバックします。WifiAwareTransport オブジェクトは iOS ターゲットにはコンパイルすらされません (@RequiresApi(Build.VERSION_CODES.S) + androidMain ソースセット)。

これが PeerTransportRouter.buildListcreateWifiAwareTransport() を呼ぶ理由です — iOS では null を返すファクトリです。

フォールバックチェーンにおける Aware の位置 {#where-aware-sits-in-the-fallback-chain}

PlainApp の PeerTransportRouter は順序付きリストです。各 senddownloadFile 呼び出しで、リストを歩き、各トランスポートを 1 つ成功するまで試します。失敗は下にカスケードします。

Diagram 2
2

なぜ Aware は「中間」で「最初」ではないのか?

LAN は利用可能ならほぼ常に速い からです。AP を経由する同じサブネットの Wi-Fi ホップは単一の 802.11 フレーム交換です。Aware データパスは NDP セットアップ (初回 ~5 秒) と、デバイス間リンク用の第 2 の Wi-Fi 無線コンテキストを追加します。両方到達可能なら LAN がレイテンシとスループットで勝ちます。

逆に BLE は 常に遅い — しかし両デバイスがペアリング済みなら常に動作します。Aware は中間です: BLE より速く、LAN より遅く、Wi-Fi がオンの Android 13+ デバイスでのみ利用可能です。

セッションライフサイクル: attach → publish + subscribe {#session-lifecycle-attach--publish--subscribe}

Wi-Fi Aware セッションは プロセス全体 です。各デバイスに正確に 1 つの WifiAwareSession があります。その中で PlainApp は 1 つの publish セッション (ピアが我々を発見できるように) と 1 つの subscribe セッション (我々がピアを発見できるように) を動かします。両者は AwareSession.start()attach コールバックを完了した瞬間に起動します。

Diagram 3
3

なぜ同じデバイスで publish と subscribe の両方を行うのか?

Wi-Fi Aware のディスカバリモデルは 非対称 です: publisher がサービスをアドバタイズし、subscriber がそれをスキャンします。ディスカバリを対称にする (両デバイスがお互いを発見する) ため、PlainApp は 両方を同時 に行います。これがなければ、デバイス A は所与のピアに対して自分が publisher か subscriber かを事前に知る必要があります — しかしピアのロールは後で clientId 比較で決まります (ディスカバリとロール割り当て を参照)。

publish と subscribe を同時に行うことで、各デバイスは相手の onServiceDiscovered を (subscriber として) 見え、かつ相手の hello メッセージを (publisher として) 受け取れます — ハンドシェイクの両方向が常に利用可能です。

終了時の自動再起動

一部の Android バリアント (特に MIUI) はバッテリー節約のため長時間動く Aware セッションをキルします。PlainApp は onSessionTerminated コールバックでこれを処理します: 終了したセッションを null にし、まだ attach されている WifiAwareSession 上で直ちに publishOwnService / subscribeOwnService を再度呼び出します。attach セッション自体は失われません — 失われるのは publish/subscribe ディスカバリセッションだけです。終了前のピアハンドルは古くなるため、awaitPeerHandlediscoveredAt タイムスタンプをチェックし、30 秒より古いハンドルを破棄します。

ディスカバリとロール割り当て {#discovery--role-assignment}

Wi-Fi Aware データパスプロトコルは 一方が publisher (サーバー) として振る舞い もう一方を subscriber (クライアント) とすることを要求します。両者が同時にイニシエータにはなれません — フレームワークは対応する相手のないリクエストを拒否します。

PlainApp は clientIds の単純な辞書式比較でロールを ピアごとに決定的 に割り当てます:

Diagram 4
4

なぜネゴシエーションではなく決定的なのか?

ネゴシエーション方式 (例: 「小さい MAC がサーバー」) は追加のメッセージ交換を要求します。辞書式比較は 冪等、対称、ステートレス です: 双方のデバイスは通信なしで同じペアに対して同じロールを計算します。clientId は 13 文字の short UUID なので、同点 (clientId == peer.id) はピアを自分自身と比較するときだけ起きます — それはトランスポートに到達しません。

ロールは下流で 2 つのことを決めます:

  1. 誰がリトライループを駆動するか。 クライアントだけが requestNetwork をリトライします。サーバーは受信した hello ごとに正確に 1 回試行します。これは 500 ms の窓のために重要です (次セクション)。
  2. 誰がポートをセットするか。 publisher は自分の HTTPS サーバーポートで着信接続を受け入れる側なので setPort(httpsPort) を呼びます。subscriber はポートをセットしません — データパス確立後に WifiAwareNetworkInfo からピアのポートを知ります。

2 フェーズハンドシェイク (hello + ready) {#the-two-phase-handshake-hello--ready}

Wi-Fi Aware データパスセットアップで最も難しいのは タイミング です。フレームワークは両者がほぼ 500 ms 以内に connectivityManager.requestNetwork を呼ぶことを要求します — 一方が相手のマッチするリクエストを登録する前に呼ぶと、フレームワークは即座に onUnavailable ("releaseRequestAsUnfulfillableByAnyFactory") で拒否します。

PlainApp はこれを Aware L2 メッセージチャネル (onServiceDiscovered が使うのと同じ sendMessage API) 上で動く 2 メッセージのアプリケーション層ハンドシェイク で解決します:

Diagram 5
5

なぜ (hello だけでなく) 2 メッセージ (hello + ready) なのか?

hello だけでは 方向の非対称性 により不十分です。subscriber は publisher を発見した瞬間 (onServiceDiscovered 内) に hello を送れますが、publisher は subscriber の PeerHandle を持つまで requestNetwork を開始できず、それは hello を受信して初めて知ります。そのため hello は 2 つの目的を果たします:

  1. subscriber の PeerHandle を publisher に届ける。 publisher は WifiAwareNetworkSpecifier を構築するためにそれが必要です。
  2. 接続の意図をシグナルする。 hello の受信は publisher に「subscriber がこれから requestNetwork するので、自分もそうすべきだ」と伝えます。

ready 受信は 逆方向 のために存在します — 「publisher が requestNetwork を登録した」と subscriber に伝えるためです。これがなければ、subscriber の requestNetwork が publisher のものより先にレースしてフレームワークに拒否される可能性があります。ready 受信は 非ブロッキングシグナル です: subscriber は requestNetwork を呼ぶ前にそれを待ちません (ラウンドトリップが増えるため)。しかし subscriber が IDLE 状態 (リトライ試行の間) にいる間に到着すれば、RETRY_DELAY_MS ギャップを待たずに即座にリトライできます。

リトライループの非対称性

これが設計で最も繊細な部分です。subscriber のみがリトライします。 publisher は受信した hello ごとに正確に 1 回の requestNetwork 試行を行います。理由は:

  • 両者が独立してリトライすると、リトライサイクルが位相からずれていき (異なる delay() 期間、異なる GC ポーズ)、2 つの requestNetwork 呼び出しは 500 ms の窓内で同時に起こることが稀になります。
  • subscriber のリトライループは各試行で新鮮な hello を送り、publishHelloListeners 経由で publisher の buildLink を再トリガーします。これにより publisher の requestNetwork は常に hello の約 50 ms 後に続き、500 ms の窓内に収まることが保証されます。

詳細は AwarePeerLink.build にドキュメント化されています。

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

requestNetwork 呼び出しは Aware トランスポートで最もタイミングセンシティブな操作です。各側で何が起きるか:

Diagram 6
6

onUnavailable コールバックの意味

onUnavailable はフレームワークがマッチするピアリクエストを見つける前に requestNetwork を拒否したときに発火します。PeerHandle 自体はまだ有効です — NDP (Neighbor Discovery Protocol) ペアリングが、相手側がまだ登録していなかったため失敗しただけです。PlainApp はこの場合に session.invalidatePeerHandle を呼びません。なぜならハンドルを無効化すると onServiceDiscovered が呼ばれたことの唯一のシグナルを破棄するからです (これは subscribe セッションライフタイム中にピアごとに 1 回発火します)。ハンドルを保持することで、リトライは新鮮なディスカバリを待たずにそれを再利用できます。

publisher 側のハンドル (onMessageReceived から) についても同様です — publisher は失敗にわたって publishPeerHandles[fromCid] エントリを保持するため、subscriber の次の hello はキャッシュされたハンドルを再利用し、ドロップされません。

ペアリング済みの各ピアは独自の AwarePeerLink オブジェクトを持ち、プロセス全体の AwareLinkPool が所有します。プールはディスカバリイベント、リンク再利用、アイドル退去を処理します。

Diagram 7
7

なぜディスカバリ時に自動ビルドしないのか?

プールは onServiceDiscovered が発火したときにリンクを ビルドしません。これは重要な決定です。混雑したカフェでは 100 台の PlainApp デバイスがすべて "plain-peer" サービスを publish しているかもしれません。各ディスカバリが requestNetwork をトリガーすると、フレームワークは NDP セットアップ試行で溢れ、Wi-Fi 無線が飽和します。

代わりにプールは PeerHandle のみを記録 し、次のいずれかを待ちます:

  1. ローカルユーザーがメッセージを送信WifiAwareTransport.sendpool.buildLink(peer) (送信側トリガー)。
  2. リモートピアが hello を送信onPublishHelloReceivedbuildLink(peer) (受信側トリガー)。
  3. リモートピアが ready を送信onSubscribeReadyReceivedbuildLink(peer) (受信側トリガー)。

これにより、リンクはユーザーが実際にメッセージを交換しているピアに対してのみビルドされます — 無線範囲内のすべての PlainApp デバイスに対してではありません。

アイドルスイープ

10 秒ごとにプールは全リンクを歩き、lastActiveAt が 60 秒より古いものを閉じます。各 senddownloadFilelink.touch() を呼び出しタイムスタンプを更新します。これにより、ユーザーがチャットをやめたピアについて Wi-Fi 無線コンテキストと OkHttp 接続プールを回収します。Android が同時に保持できる Aware データパス数をほぼ 4〜10 (デバイス依存) に制限しているため重要です。

IPv6 アドレッシングと plain-aware-peer DNS のトリック {#ipv6-addressing--the-plain-aware-peer-dns-trick}

Wi-Fi Aware データパスは リンクローカル IPv6 のみ を使います。IPv4 も DNS サーバーも DHCP もありません。ピアの IPv6 アドレスは onCapabilitiesChanged 内の WifiAwareNetworkInfo.peerIpv6Addr フィールド経由で配信されます — Aware ネットワークインターフェース上でのみ意味を持つ fe80::... アドレスです。

PlainApp はこのアドレスに HTTPS リクエストを送る必要がありますが、OkHttp の https:// URL パースはホスト名に生 IPv6 リテラルを拒否します (https://[fe80::abcd]:8443/ は動きますが、カスタム Dns リゾルバー経由でルーティングする方がクリーンです)。トリック:

Diagram 8
8

なぜセンチネルホスト名なのか?

代替案 — IPv6 リテラルを直接 URL に渡す — は、すべての呼び出し側がリンクローカルアドレスについて知ることを要求します。センチネルホスト名を使うことで、URL 構築は LAN でも Aware でも同一になります: 両者は OkHttp がパースできる有効な https://<host>:<port>/peer_graphql URL を生成します。唯一の違いはクライアントにバインドされた Dns 実装です — LAN はシステム DNS を使い、Aware はセンチネルホスト名に対してはキャッシュされたリンクローカルアドレスを返し、それ以外については Dns.SYSTEM にフォールスルーする awareDns(peerIpv6) を使います。

なぜ network.socketFactory なのか?

Android の Network オブジェクトは特定のネットワークインターフェース (この場合は Aware データパス) を表します。network.socketFactory を呼び出して OkHttp の socketFactory 設定に渡すことで、すべての TCP ソケットが Aware インターフェース上で作られることを強制します — デフォルトの Wi-Fi やセルラーインターフェース ではなく。これがないと OS はデフォルトネットワーク経由でリクエストをルーティングし、そこではリンクローカル IPv6 が到達不能で、リクエストは ENETUNREACH で失敗します。

暗号: PMK 派生と ChaCha20 再利用 {#cryptography-pmk-derivation--chacha20-reuse}

Wi-Fi Aware はデータパス向けにオプションの PMK (Pairwise Master Key) をサポートします。セットすると、L2 リンク自体がその PMK で暗号化されます — Wi-Fi 無線が暗号化を処理し、アプリケーション層の暗号は不要です。

PlainApp は LanTransportBleTransport がアプリケーション層暗号化に使うのと 同じ ChaCha20 共通鍵 から PMK を派生します:

Diagram 9
9

なぜ 32 バイトに切り詰めるのか?

Wi-Fi Aware PMK は正確に 32 バイト (256 bit) でなければなりません。ペアリングの ChaCha20 共通鍵も通常ケースでは 32 バイトなので、raw.size == 32 ブランチが一般的なパスです。切り詰め/パディングのフォールバックは、鍵が短く保存された (理論上の) ケースを扱います — ゼロで 32 バイトにパディングするのは防御的措置であり、正しくペアリングしたピアでは実際には起きません。

署名エンベロープは LAN と同一

createCryptoHttpClientLanTransport が使うのと同じファクトリであるため、Aware 上の L7 暗号は LAN と バイト完全に同一 です。サーバー側の PeerGraphQLService はどのトランスポートがリクエストを配信したかを知りません (気にもしません) — 署名付きで暗号化された GraphQL ペイロードを見て、ピアの共通鍵で復号するだけです。これが Chat Architecture でドキュメント化された「ひとつのコードベース、多くのトランスポート」原則です。

メッセージ送信パス (エンドツーエンド) {#message-send-path-end-to-end}

すべてを組み合わせると — チャットメッセージが Wi-Fi Aware 経由で送信されるとき何が起きるか:

Diagram 10
10

注目すべき設計選択

  • 接続再利用。 各リクエスト後に GATT 接続を破棄する BleTransport と違い、WifiAwareTransport は 60 秒のアイドル窓内でユーザーが行うだけのリクエストに対して Aware データパスを再利用 します。最初のリクエストは ~400 ms のハンドシェイクを払います。以降のリクエストは ~10 ms ラウンドトリップです。
  • LAN と同じ暗号。 ChaCha20 インターセプターと署名エンベロープは LAN とバイト同一です。ピアの PeerGraphQLService はどのトランスポートがリクエストを配信したかを知りません。
  • リンク失敗時に preemption なし。 buildLink が失敗するとトランスポートは TransportUnavailable をスローし、ルーターは BLE にフォールスルーします。send 内にリトライはありません — AwarePeerLink.build はすでに独自の内部リトライループ (MAX_BUILD_ATTEMPTS = 1、prewarmer が両側をプライムしていればそれ以上) を行っています。

ファイルダウンロードパス (エンドツーエンド) {#file-download-path-end-to-end}

Aware 経由のファイルダウンロードはチャットメッセージと同じデータパスを再利用しますが、大きなファイルのストリーミング向けに設定した 別の OkHttp クライアント を使います:

Diagram 11
11

なぜダウンロード向けに別のクライアントなのか?

チャットクライアント (AwareHttpClientFactory.build) は 30 秒の requestTimeoutMillis を持ちます — GraphQL ミューテーションには適切ですが 100 MB のファイルダウンロードには壊滅的です。ダウンロードクライアント (buildFileDownload) は次を設定します:

  • connectTimeoutMillis = 10_000 (チャットの 5 秒より長く、新鮮なデータパス上の遅い最初のパケットに寛容)
  • readTimeout = 120 秒 per read (暗黙のデフォルト 10 秒に対して)
  • requestTimeoutMillis = 120_000 (2 分 — ほとんどのファイルに十分)
  • retryOnConnectionFailure(true) — ダウンロード中の read ドロップは転送全体を失敗させるのではなくリトライされる

また ChaCha20 インターセプターを省略 します。/fs エンドポイントは raw ファイルバイトを提供し (署名付き GraphQL エンベロープではなく)、L2 PMK (存在するとき) がすでに無線リンクを暗号化しています。50 MB の動画をソフトウェアで ChaCha20 二重暗号化すると CPU を無駄にし転送が遅くなります。

バッファリングではなくストリーミング

BLE パスと同様、Aware ダウンロードはファイルを ByteReadChannel 経由でストリーミングします — ファイルはバイトが到着したままテンポラリファイルに書き込まれ、メモリにバッファリングされません。PeerFileDownloader は 8 KB チャンクを読み、毎秒プログレスイベントを発します。同じ DownloadedResponse / PeerFileDownloader / DownloadQueue パイプラインがすべてのトランスポートにわたって再利用されます — トランスポート固有なのは channel ソースだけです。

Prewarming: BLE トリガーの Aware 起動 {#prewarming-ble-triggered-aware-startup}

Aware パスで最もユーザーに見えるレイテンシは 最初のハンドシェイク です — 両者がまだ Aware を起動していない場合、ユーザーの最初のメッセージは次を待つ必要があります:

  1. ローカル Aware セッション attach (~1 秒)
  2. ローカル publish + subscribe 開始 (~1 秒)
  3. リモートピアの Aware 起動 (~2 秒 over BLE)
  4. 相互ディスカバリ (~1 秒)
  5. NDP ハンドシェイク (~400 ms)

最初のバイトが送られるまでに約 5 秒です。このレイテンシを隠すため、PeerTransportPrewarmerChatPage 入場時に動き、BLE 経由でリモートピアの Aware 起動をトリガー します:

Diagram 12
12

BLE の二重の役割

BLE はここで 2 つの目的を果たします:

  1. ピアの現在の Aware 状態を読む (安価、GATT 接続なし — スキャンレスポンスの serviceData byte0 が Aware フラグを運ぶ)。
  2. ピアが Aware をサポートしているが現在動いていない場合、起動をトリガーする。 これは通常の BleTransport.send パス経由です — 共通 ChaCha20 鍵で暗号化された startAware GraphQL ミューテーションが、GATT RPC 経由でピアの /peer_graphql エンドポイントに配信されます。

これはトランスポートが単にフォールバックするのではなく 協調 する数少ない場所のひとつです: BLE はユーザーが気づく前に、セッションをより高速な Aware トランスポートに 先制的にアップグレード するために使われます。

なぜ楽観的に setAwareRunning(true) するのか?

startAware ミューテーションはリモートピアのリゾルバーが WifiAwareTransport.start() を呼び出した時点で成功を返します — しかし Aware セッションはまだ実際には attach されていません (onAttached は非同期に発火)。PlainApp はピアを楽観的に awareRunning = true とマークします。なぜなら:

  • 実際に起動したなら、次の send は Aware を使います (高速)。
  • 起動しなかったなら (例: ピアの Wi-Fi がオフ)、次の sendbuildLinkTransportUnavailable で失敗し、自然に BLE にフォールバックします。
  • 偽陽性のコストは ~5 秒のタイムアウト 1 回で、恒久的なブロックではありません — PeerCircuitBreaker は失敗を記録しますが BLE レッグはオープンしません (BLE は自身の失敗でのみオープンします)。

なぜ 30 秒にスロットルするのか?

PeerTransportPrewarmer.prewarm(peerId) はピアごとにタイムスタンプを記録し、30 秒以内の再実行を拒否します。これはユーザーがチャット一覧とチャットページを頻繁に行き来するためです — スロットルしないと、ナビゲーションのたびに BLE スキャン + startAware ミューテーションがトリガーされ、バッテリーを消費し BLE 無線をスパムします。30 秒の窓は、ちょうどオンラインになったピア (例: リモートデバイスでアプリを開いた) を捉えるのに十分短く、スプリアスな再実行を避けるのに十分長いです。

失敗モードとファストスキップフラグ {#failure-modes--the-fast-skip-flag}

Aware は他のどのトランスポートよりも失敗モードが多いです。isAwareRunning ファストスキップフラグはモジュール全体で最も重要な最適化です — これがないと、毎回の send が BLE にフォールバックする前に buildLink のタイムアウトで 10 秒を無駄にします。

Diagram 13
13

isAwareRunning フラグが鍵

この単一の boolean がないと、すべての Aware send は次のいずれかになります:

  • 常に buildLink を試行 → Aware が動いていないピアへの毎回の送信で 10 秒のタイムアウト。
  • 常に Aware をスキップ → 両側で動いていても使われない。

フラグは 2 つのソースから、権威の順に更新されます:

  1. BLE スキャンレスポンス (安価、GATT 接続なし) — PeerTransportPrewarmer.refreshAwareFlagFromScan がセット。ピアは 9 バイトの serviceData ペイロード (byte0 ビットフィールド) で Aware 状態をアドバタイズ。
  2. GATT DISCOVER 返信 (権威あり) — 完全なディスカバリが起きたとき PairingTransport.scanAndDiscover がセット。これはスキャンヒントを上書きする。

false のとき、WifiAwareTransport.senddownloadFile即座に TransportUnavailable をスローします — スキャンも、ハンドシェイクも、タイムアウトもありません。ルーターはマイクロ秒で BLE にフォールスルーします。

主要定数リファレンス {#key-constants-reference}

定数場所目的
AwareSession.SERVICE_NAME"plain-peer"ディスカバリすべての PlainApp デバイスが publish/subscribe するサービス名
AwareSession.PEER_HANDLE_MAX_AGE_MS30 000PeerHandle キャッシュ古いハンドルを破棄 (ピアの publish セッションが再起動されている可能性)
AwareSession.READY_TIMEOUT_MS15 000ハンドシェイクpublisher の ready 受信に対する subscriber の待機
AwareSession.MSG_HELLO0ハンドシェイクSubscriber → Publisher のメッセージ ID
AwareSession.MSG_READY1ハンドシェイクPublisher → Subscriber のメッセージ ID
AwarePeerLink.MAX_BUILD_ATTEMPTS1ハンドシェイク (クライアントのみ)単一試行 — 以前は 3、prewarmer が両側をプライムするため 1 に
AwarePeerLink.ATTEMPT_TIMEOUT_MS5 000ハンドシェイク試行ごとのタイムアウト — 以前は 10 秒、フォールバック高速化のため半減
AwarePeerLink.RETRY_DELAY_MS500ハンドシェイクリトライ試行間の遅延 (クライアントのみ)
AwarePeerLink.REQUEST_TIMEOUT_MS30 000NDPconnectivityManager.requestNetwork のタイムアウト
AwareLinkPool.IDLE_TIMEOUT_MS60 000プールスイープ60 秒の非アクティブ後にアイドルリンクをクローズ
AwareLinkPool.IDLE_SWEEP_INTERVAL_MS10 000プールスイープスイープ間隔
AwareHttpClientFactory.AWARE_HOST"plain-aware-peer"DNSカスタム Dns でピア IPv6 に解決されるセンチネルホスト名
build チャットクライアントconnectTimeout 5 秒、requestTimeout 30 秒、ChaCha20 インターセプター
buildFileDownloadconnectTimeout 10 秒、readTimeout 120 秒、requestTimeout 120 秒、暗号化なし
PeerTransportPrewarmer.PREWARM_TTL_MS30 000Prewarmピアごとのスロットル
PeerTransportPrewarmer.BLE_SCAN_TIMEOUT_MS15 000PrewarmrefreshAwareFlagFromScan の BLE スキャンタイムアウト
PeerCircuitBreaker.WINDOW_MS30 000サーキットブレーカーしきい値後のオープン期間
PeerCircuitBreaker.MAX_FAILURES2サーキットブレーカーオープンするまでの窓内失敗数
TempData.httpsPort8443 (デフォルト)サーバーWifiAwareNetworkSpecifier.setPort でアドバタイズされる publisher のポート
BleServiceData.AWARE_SUPPORTED0x01BLE スキャンレスポンスピアが Wi-Fi Aware をサポートしていることを示すビット
BleServiceData.AWARE_RUNNING0x02BLE スキャンレスポンスピアの Aware サービスが現在動いていることを示すビット

設計トレードオフのまとめ {#design-trade-offs-recap}

Diagram 14
14

関連記事

  • Chat ArchitectureWifiAwareTransportLAN → Aware → BLE フォールバックチェーンや広範なチャット送受信パイプラインにどうフィットするか。
  • BLE Transport — Aware が利用不可のときに引き継ぐ最後の手段のトランスポート。prewarmer がリモートピアの Aware 起動をトリガーするために使うチャネルでもある。
  • Pairing Flow — Aware PMK として再利用される共通 ChaCha20 鍵がどう確立されるか。BLE スキャンレスポンスフラグ (AWARE_SUPPORTED / AWARE_RUNNING) がどう設定されるか。