Multi-source state probe for the battery bench.
When a battery read fails, we want to know why: is the BEAM dead, just unreachable, suspended in the background? The probe walks a short pipeline of network checks and returns a typed state. The bench uses this to produce informative trace lines and decide whether to attempt reconnection.
State derivation
epmd_reachable? Node.connect rpc_ping → state
────────────── ───────────── ────────────── ─────
false — — :unreachable
true false — :alive_epmd_only
true true timeout :alive_dist_only
true true ok :alive_rpcWhen a hw_udid is provided and ideviceinfo is available, we additionally
probe USB battery readiness:
ideviceinfo battery → :usb_ok | :usb_failed | :no_usbAnd app-process liveness via xcrun devicectl device info processes:
app_pid_alive? → :app_running | :app_dead | :app_unknownAll probes are independent and failure-tolerant — any single probe failing doesn't crash the whole snapshot.
Summary
Types
Foreground/background/dead, or unknown if we can't tell.
Screen state, where derivable. :unknown is the honest default.
USB battery readiness via ideviceinfo.
Functions
Format the probe result as a one-line trace fragment.
Run the full probe and return a populated state struct.
Types
@type app_process() :: :app_running | :app_suspended | :app_dead | :app_unknown
Foreground/background/dead, or unknown if we can't tell.
@type reachability() ::
:alive_rpc | :alive_dist_only | :alive_epmd_only | :unreachable
:alive_rpc— RPC just succeeded; BEAM is fully responsive:alive_dist_only— Node.connect works, RPC times out (suspended?):alive_epmd_only— TCP to EPMD works, dist refused (BEAM up but no dist):unreachable— Phone offline or BEAM dead
@type screen() :: :on | :off | :unknown
Screen state, where derivable. :unknown is the honest default.
@type t() :: %MobDev.Bench.Probe{ app_process: app_process(), battery_pct: integer() | nil, reachability: reachability(), reason: String.t() | nil, screen: screen(), ts_ms: integer(), usb: usb() }
@type usb() :: :usb_ok | :usb_failed | :no_usb
USB battery readiness via ideviceinfo.
Functions
Format the probe result as a one-line trace fragment.
iex> probe = %MobDev.Bench.Probe{
...> ts_ms: 0, reachability: :alive_rpc, app_process: :app_running,
...> usb: :no_usb, screen: :off, battery_pct: 87, reason: nil
...> }
iex> MobDev.Bench.Probe.format(probe)
"screen:off app:running rpc:ok battery:87%"
Run the full probe and return a populated state struct.
Common options:
:platform—:ios(default) or:android— selects which USB / app- process probes to run:node— node atom to probe (required for dist/RPC checks):host— IP/host for EPMD probe (defaults to host portion of:node):rpc_timeout_ms— defaults to 2000:tcp_timeout_ms— defaults to 1000:expected_screen—:on | :off | :unknown— what we believe the screen state to be (e.g. afterlock_screen). Recorded with the snapshot.
iOS-specific:
:hw_udid— hardware UDID forideviceinfoUSB probe:device_id— CoreDevice UUID fordevicectlprocess check:app_pid— pid launched at bench start; checked againstdevice_id
Android-specific:
:adb_serial— ADB serial / IP:port foradb shellbattery + process probes:bundle_id— app bundle identifier for the process-running check