MobDev.Tunnel (mob_dev v0.6.8)

Copy Markdown View Source

Manages port tunnels for Android and physical iOS devices.

Android (adb): adb reverse tcp:4369 tcp:4369 — Android BEAM registers in Mac's EPMD adb forward tcp:<dist> tcp:<dist> — Mac reaches the device's dist port (1:1)

Physical iOS (direct networking — USB preferred, WiFi/LAN fallback): mob_beam.m finds the device's own IP via getifaddrs() and starts the BEAM as mob_qa_ios@<device-ip>. The in-process EPMD binds 0.0.0.0:4369 so Mac can query it at <device-ip>:4369. The dist port is directly reachable.

iOS simulator: Shares Mac network stack — no tunnels needed.

Dist ports are keyed by device serial, not run index

The Mac runs ONE EPMD (port 4369) that every device — across every project and every mix mob.connect run — registers into. Assigning dist ports by per-run index (9100 + index) meant project A's device-0 and project B's device-0 both claimed 9100: two nodes at the same port in the shared EPMD, but adb forward tcp:9100 can only point at one device → the other resolved to the wrong phone or nothing (silent timeout). Now the port is derived from the device serial (serial_base_port/1, a crc32 hash into 9100..9899), so a given phone always gets the same unique port regardless of project/run, and assign_dist_port/2 bumps past any port another live node/forward already holds (cross-project or hash collision).

Summary

Functions

The serial's base port, bumped to the next free slot if in_use already claims it (a cross-project collision or a crc32 hash collision between two serials). Walks the window from the base; falls back to the base if the whole window is somehow taken. Pure — in_use is gathered by the caller.

Stable, deterministic dist port for a device serial — a crc32 hash into [9100, 9100 + 800). Same serial → same port across runs and projects, so the port a device is deployed to listen on matches what mix mob.connect later forwards to.

Assigns a serial-derived dist port and sets up tunnels for a device.

Tears down tunnels for a device.

Functions

assign_dist_port(serial, in_use \\ MapSet.new())

@spec assign_dist_port(String.t(), MapSet.t()) :: pos_integer()

The serial's base port, bumped to the next free slot if in_use already claims it (a cross-project collision or a crc32 hash collision between two serials). Walks the window from the base; falls back to the base if the whole window is somehow taken. Pure — in_use is gathered by the caller.

serial_base_port(serial)

@spec serial_base_port(String.t()) :: pos_integer()

Stable, deterministic dist port for a device serial — a crc32 hash into [9100, 9100 + 800). Same serial → same port across runs and projects, so the port a device is deployed to listen on matches what mix mob.connect later forwards to.

setup(device)

@spec setup(MobDev.Device.t()) :: {:ok, MobDev.Device.t()} | {:error, String.t()}

Assigns a serial-derived dist port and sets up tunnels for a device.

Cleans the device's own stale forwards first, then picks a port that no other live node/forward on this Mac is using. Returns {:ok, %Device{}} with dist_port (and host_ip for USB iOS) filled in, or {:error, reason}.

teardown(device)

@spec teardown(MobDev.Device.t()) :: :ok

Tears down tunnels for a device.