◉ANTIGRAVITY LABEN
記事一覧/アプリ開発
▣ アプリ開発/2026-07-17上級

Android 17 のローカルネットワーク権限 — LAN に触る箇所を機械で洗い出してから、エージェントに直させる

新しい API レベルではローカルネットワークへの明示的な許可が必要になります。grep では 11 経路のうち 2 つしか見つかりませんでした。経路の種類で拾う検出器と、エージェントに渡せる作業単位への畳み方を実測とともに。

Android 17権限設計11移行12エージェント73ローカルLLM22

✦ プレミアム記事

設定画面のホストは何も変えていないのに、アプリから手元の Ollama につながりません。

そういう報告が来たとき、まず疑うのは自分の設定ミスです。IP を打ち直し、ポートを確認し、ブラウザからは普通に応答が返ってくることを見て、それでも繋がりませんでした。ログには例外すら残っていません。ただ、レスポンスが空で返ってきているだけでした。

新しい API レベルを対象にしたアプリは、ユーザーの明示的な許可なしにローカルネットワークへアクセスできなくなります。厄介なのは、この失敗が例外ではなく「何もなかったこと」として返ってくる形をとりやすい点です。探索は 0 件で成功し、ソケットはタイムアウトし、Cast の一覧は空のまま。どれも、それ単体では正常な結果と見分けがつきません。

個人開発でアプリを保守していると、この種の変更はいつも「動かなくなってから気づく」やり方で処理しがちです。私自身、Android 16 の edge-to-edge の強制でそれをやりました。今回は先に棚卸しをしておきたい、と思って手を動かした記録です。

何が変わるのか、そして何を先に決めるべきか

対象は、新しい API レベルをターゲットにしたアプリです。ローカルネットワーク(同一 LAN 上の他の端末)への通信に、明示的なユーザー許可が要るようになります。インターネット越しの通信は従来どおりで、影響を受けるのは「家庭内や社内のネットワークの中で完結する通信」だけです。

手元の環境では android.permission.LOCAL_NETWORK_ACCESS という名前で扱われています。ただし名称も挙動もプラットフォームの世代で動きうるところなので、最終的な確認は Android Developers のバージョン別リリースノート で取ってください。この記事で固定したいのは権限の名前ではなく、自分のアプリのどこが LAN に触っているかを、記憶ではなく機械で答えられる状態にすることです。

影響範囲の当たりをつけるために、まず「LAN に出る経路」を書き出しました。

経路コードに現れる形権限なしでどう見えるか
私的アドレス直打ち192.168.x.x / 172.16-31.x.x / 10.x.x.x接続タイムアウト
mDNS 名前解決ollama.local など名前が解決しない
NSD によるサービス探索NsdManager.discoverServices0 件で成功する
マルチキャスト受信createMulticastLockロックは取れるが何も届かない
Cast / MediaRouteraddCallback の能動スキャン端末一覧が空のまま
UDP ブロードキャスト255.255.255.255 宛の送信応答が来ない
設定値のホストbaseUrl を実行時に受け取るユーザーの入力次第で変わる
WebViewLAN の URL を loadUrl読み込み失敗

この表を作った時点で、気になったことがひとつありました。右の列の大半が「エラーではない」のです。0 件、空リスト、タイムアウト。どれも、ネットワークが遅いときや相手が起動していないときと同じ顔をしています。つまり、権限が下りていないことをアプリ側は知らないまま、ユーザーには「見つかりませんでした」とだけ表示し続けることになるのです。

grep が見つけたのは、11 経路のうち 2 つでした

棚卸しの最初の一手は、当然 grep '192.168' です。手元のアプリで実際に使っている LAN 通信の書き方を 11 種類ぶん、検証用のツリーに並べて試しました。ファイルは 11 個、経路は 11 種類、すべてに「ここが LAN に出る」という意図を持たせてあります。

$ grep -rn "192\.168" --include="*.kt" --include="*.xml" .
./app/src/main/res/xml/network_security_config.xml:3:  <domain includeSubdomains="true">192.168.0.0</domain>
./app/src/main/java/net/example/app/Defaults.kt:4:  const val OLLAMA_FALLBACK = "http://192.168.11.8:11434"

コメント行を除いて 2 件。11 経路のうち 2 つ、率にして 18% です。

範囲を広げても大きくは変わりませんでした。私的アドレスの全レンジを書いた正規表現でも 5 件どまり、45% です。

$ grep -rnE "(10\.|192\.168\.|172\.(1[6-9]|2[0-9]|3[01])\.|169\.254\.)[0-9]" \
    --include="*.kt" --include="*.xml" . | wc -l
5

残りの 6 経路には、そもそも IP が書かれていません。NsdManager で探索するコードにも、MediaRouter で Cast 端末を探すコードにも、設定画面で入れたホストを使う OkHttp のラッパにも、数字の並びは一つも出てきません。LAN に出るかどうかはコードの見た目ではなく、実行時にどこへ向かうかで決まります。grep はそこを見られません。

見つからないだけならまだ救いがあります。困るのは、grep で 2 件見つかったときに「2 箇所直せば終わり」という感触が残ることです。手元の感覚として、これはかなり危ういと感じています。数字が出てしまうと、それが全体だと思ってしまうのです。

✦

ここまでお読みいただきありがとうございます。

この記事の続きを読む

この先には、実装コードやベンチマーク結果など、実務でお役に立てる内容をご用意しています。このサイトは広告を掲載しておらず、サーバーや開発にかかる費用はメンバーの皆様のご支援で成り立っています。もしお役に立てていましたら、ご支援いただけますと大変ありがたいです。

この記事で得られること
✦grep '192.168' が見つけたのは 11 経路中 2 つ。mDNS・Cast・設定値ホストには IP が現れない
✦経路の種類で拾う検出器 lan_scan.py(確度3段階・権限未宣言なら exit 1)の全文と実測結果
✦「Android 17 対応して」を 11 個の確認可能なタスクに畳んで渡す lan_tasks.py
Stripe による安全な決済 · いつでもキャンセル可能
✦

この記事を購入する

この先の内容をすべてお読みいただけます。一度のご購入で、いつでも何度でもアクセスできます。このサイトは広告を掲載しておらず、皆さまのご支援がサーバー費用などの運営を支えています。

または
メンバーシップなら全記事が読み放題 →
シェア

お読みいただきありがとうございます

Antigravity Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • ✦コピー&ペーストで使える実装コード付き
  • ✦毎日新しい上級ガイドを追加
  • ✦¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

関連記事

⬡ 連携・プラグイン2026-10-07
画像モデルの終了日は 10月29日 — エージェント構成の「画像を呼ぶ箇所」を洗い出して、戻り先つきで切り替える
Gemini API の deprecations 表で、画像モデル gemini-3.1-flash-image に 10月29日の終了日が付いていました。設定ファイルやコードに散らばった呼び出し箇所を台帳と突き合わせ、切り替え日と戻り先を決めておくまでを、動くスクリプトで残します。
◈ Agents & Manager2026-09-24
無人実行のエージェントが設定ファイルを書き換えたら戻す、小さな番人スクリプトの組み方
読み取り専用にしたはずの枠をエージェント自身が設定ファイルごと広げてしまう、という報告を読んで、夜間の無人実行を外側から包む番人を組みました。実行前後の指紋比較・退避と復元・打ち切り時間の3点を、動くコードでお伝えします。
◈ Agents & Manager2026-08-16
Canary 継続配信になった Android 17 で、エージェントには「報告しない条件」から先に渡しました
Android 17 が Developer Preview をやめて Canary の継続配信に切り替わり、検証の締め切りが自分の側に移りました。更新を追う担当をエージェントへ渡すにあたり、報告の条件ではなく沈黙の条件から設計した記録です。