MobDev.Bench.Probe (mob_dev v0.5.2)

Copy Markdown View Source

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_rpc

When a hw_udid is provided and ideviceinfo is available, we additionally probe USB battery readiness:

ideviceinfo battery  :usb_ok | :usb_failed | :no_usb

And app-process liveness via xcrun devicectl device info processes:

app_pid_alive?    :app_running | :app_dead | :app_unknown

All 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.

t()

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

app_process()

@type app_process() :: :app_running | :app_suspended | :app_dead | :app_unknown

Foreground/background/dead, or unknown if we can't tell.

reachability()

@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

screen()

@type screen() :: :on | :off | :unknown

Screen state, where derivable. :unknown is the honest default.

t()

@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()
}

usb()

@type usb() :: :usb_ok | :usb_failed | :no_usb

USB battery readiness via ideviceinfo.

Functions

format(p)

@spec format(t()) :: String.t()

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

snapshot(opts \\ [])

@spec snapshot(keyword()) :: t()

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. after lock_screen). Recorded with the snapshot.

iOS-specific:

  • :hw_udid — hardware UDID for ideviceinfo USB probe
  • :device_id — CoreDevice UUID for devicectl process check
  • :app_pid — pid launched at bench start; checked against device_id

Android-specific:

  • :adb_serial — ADB serial / IP:port for adb shell battery + process probes
  • :bundle_id — app bundle identifier for the process-running check