Hotspot
IWifiHotspot raises an access point other devices can join. What that access point is differs
sharply by platform, and the differences matter to your UI.
What you get where
Section titled “What you get where”| Android | iOS / macOS | Windows | Linux | |
|---|---|---|---|---|
| Hotspot | ✅ local-only | ❌ no API | ✅ full tethering | ✅ AP mode |
| Internet for clients | ❌ | — | ✅ shares the machine’s connection | ✅ NAT via ipv4.method=shared |
| You choose the SSID/passphrase | ❌ OS generates them | — | ✅ | ✅ |
| Client list | ❌ | — | ✅ | ✅ |
- Android raises a local-only hotspot. Clients can reach the device but get no route to the
internet. Real tethering lives behind
TETHER_PRIVILEGED, a signature permission that is not reachable from an app-store build at any API level. - iOS and macOS expose no hotspot API at all — not even a way to read whether Personal Hotspot
or Internet Sharing is running.
IsSupportedis false andStartthrows. - Windows shares whatever internet connection the machine currently has, so starting one while offline fails.
- Linux runs NetworkManager AP mode with a DHCP server and NAT on the interface.
Starting one
Section titled “Starting one”if (!hotspot.IsSupported) return; // iOS and macOS
await using var session = await hotspot.Start( new HotspotConfiguration { Ssid = "shiny-setup", Passphrase = "letmein12345", Band = WifiBand.TwoPointFourGhz }, ct);
// ALWAYS read back what is actually runningShowToUser(session.Info.Ssid, session.Info.Passphrase);HotspotConfiguration is entirely optional — await hotspot.Start(ct: ct) lets the platform choose
everything. Band is honoured on Windows (2.4/5 GHz only) and Linux; 6 GHz falls through to the
platform default everywhere.
The session is the lifetime
Section titled “The session is the lifetime”IHotspotSession is not a handle to the hotspot, it is the hotspot’s lifetime:
await using var session = await hotspot.Start(config, ct);// ... access point is up ...// disposal brings it downAndroid tears its reservation down the moment the owning process drops it, so letting the session go
out of scope without disposing leaves the hotspot up only until your process exits. Stop() is
equivalent and safe to call more than once.
Only one hotspot runs at a time — calling Start again stops the previous one first rather than
silently handing back a hotspot with settings you did not ask for.
hotspot.Changed += (_, info) =>{ // info is the running hotspot's settings, or null when it stops};Connected clients
Section titled “Connected clients”if (wifi.Capabilities.HasFlag(WifiCapabilities.HotspotClients)){ foreach (var client in await session.GetClients(ct)) Console.WriteLine($"{client.MacAddress} {client.IpAddress} {client.HostName}");}HotspotClient carries MacAddress, IpAddress and HostName. Which of those are populated
depends on how the platform learns about the client:
- Windows reports the MAC and whatever host names it has resolved.
- Linux reads the kernel neighbour table (
/proc/net/arp) filtered to the hotspot interface, so it always has a MAC and an address but rarely a name. - Android throws
WifiNotSupportedException. It has never exposed a client list for a local-only hotspot, and the ARP table apps used to read instead has been unreadable since Android 10.
Raising a hotspot takes the radio
Section titled “Raising a hotspot takes the radio”On most adapters, AP mode and station mode are mutually exclusive: bringing the access point up drops the device off whatever network it was joined to. Some chipsets support concurrent station/AP and some do not, and there is no portable way to ask in advance. Assume the device goes offline, and warn the user before you start.
On Linux specifically, a few cheap USB adapters have drivers with no AP mode at all. That fails at
activation with a WifiException carrying NetworkManager’s own message, not up front.


