ANTIGRAVITY LABJP
Articles/App Development
App Development/2026-07-17Advanced

Android 17's Local Network Permission: Inventory Every LAN Call Site Before You Hand the Fix to an Agent

Apps targeting the new API level need explicit permission to reach the local network. A grep for private IPs found 2 of my 11 LAN paths. Here is the detector that found all 11, and how I turned its output into eleven checkable tasks for an agent.

Android 17PermissionsMigration5Agents21Local LLM7

Premium Article

The host in settings had not changed, yet the app could no longer reach my local Ollama instance.

When that report came in, my first suspect was my own configuration. I retyped the IP. I checked the port. A browser on the same machine answered the endpoint without complaint. Still nothing from the app — and no exception in the log either. Just an empty response, returned politely.

Apps targeting the new API level can no longer reach the local network without an explicit grant from the user. What makes this awkward is the shape of the failure: it arrives not as an error but as an absence. Discovery succeeds with zero results. Sockets time out. The Cast device list stays empty. None of those, on their own, look any different from a slow network or a server that simply is not running.

Maintaining apps as an indie developer, I usually handle changes like this the lazy way — I notice them after something breaks. That is exactly how Android 16's edge-to-edge enforcement went for me. This time I wanted the inventory first, so I sat down and did the work.

What changes, and what to decide first

The scope is apps targeting the new API level. Traffic to other devices on the same LAN now requires an explicit user grant. Internet traffic is untouched; only communication that stays inside a home or office network is affected.

On my machine the permission goes by android.permission.LOCAL_NETWORK_ACCESS. Both the name and the behaviour can shift between platform generations, so treat the Android Developers release notes as the authority. What I want to pin down here is not the permission string but something more durable: being able to answer "where does my app touch the LAN?" from a machine rather than from memory.

To size the blast radius, I first wrote out every way an app leaves for the LAN.

PathHow it appears in codeWhat you see without permission
Private address literal192.168.x.x / 172.16-31.x.x / 10.x.x.xConnection timeout
mDNS name resolutionollama.local and friendsName never resolves
NSD service discoveryNsdManager.discoverServicesSucceeds with zero results
Multicast receivecreateMulticastLockLock acquires, nothing arrives
Cast / MediaRouterActive scan via addCallbackDevice list stays empty
UDP broadcastSends to 255.255.255.255No replies
Host from settingsbaseUrl supplied at runtimeDepends on what the user typed
WebViewloadUrl against a LAN URLLoad failure

One thing struck me the moment the table was finished: almost nothing in that right-hand column is an error. Zero results. Empty list. Timeout. Every one of them wears the same face as a slow network or an offline peer. The app never learns that permission was withheld, and the user keeps seeing "nothing found" forever.

Grep found 2 of my 11 paths

The obvious first move is grep '192.168'. I laid out the eleven ways my own apps actually talk to the LAN in a test tree — eleven files, eleven distinct paths, each one deliberately reaching the local network.

$ 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"

Two hits once comment lines are excluded. Two out of eleven — 18%.

Widening the net barely helped. A regex covering every private range still only reached five, or 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

The remaining six paths contain no IP address at all. Not in the NsdManager discovery code, not in the MediaRouter scan, not in the OkHttp wrapper that uses whatever host the settings screen handed it. Whether a call leaves for the LAN is decided by where it points at runtime, not by how it looks on the page. Grep cannot see that.

Missing them would be bad enough. The worse part is the feeling those two hits leave behind — that two edits will do it. Once a number appears, you tend to treat it as the whole picture.

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
A grep for '192.168' caught 2 of 11 LAN paths — mDNS, Cast, and configurable hosts carry no IP literals at all
Full source for lan_scan.py: route-shaped detection, three confidence tiers, exit 1 when the permission is undeclared
lan_tasks.py turns 'make it Android 17 ready' into eleven tasks an agent has to answer one by one
Secure payment via Stripe · Cancel anytime

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Antigravity Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

Related Articles

App Dev2026-06-20
Agent Config Drifts Quietly Across Environments: Detection and Correction
Across two Macs and an automation host, agent settings slowly diverge and only one side fails. Here is how to surface that config drift with normalized hashing and a correction workflow, from an indie developer's setup.
App Dev2026-07-15
Quarantining the Dependencies Your Agent Adds, Before They Install
When an agent adds a dependency overnight, nobody reviews the lifecycle scripts that run at install time. Here is how I turned the default off and built a quarantine score to let the safe ones through.
App Dev2026-07-14
Protecting Screenshot Tests for AdMob Screens from Ad Nondeterminism
Screens with ads turn red on every screenshot run, and eventually nobody reviews the diffs. Here is a design that seals off AdMob banner nondeterminism and leaves only real layout breaks in your checks, with Compose code and Antigravity-driven diff triage.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →