MobDev.DeviceLeases (mob_dev v0.7.21)

Copy Markdown View Source

agent-device device claims, read as leases, so a task that picks devices on its own leaves alone a device another agent has claimed (MOB-330).

Several agents share one machine's phones, emulators and simulators, and each claims the device it works on with agent-device open --serial <s> --session <name>. Only agent-device enforces those claims; adb and the mob tasks never saw them, so a bare mix mob.deploy once installed and launched an app on an emulator another agent was driving.

The claims come from agent-device device status --json, which reads the host-local claim records without contacting the daemon or any device. When agent-device is not on PATH there are no claims and selection is unchanged.

A claim is yours when its session equals AGENT_DEVICE_SESSION, the variable agent-device itself reads for the session name. With the variable unset every claim belongs to someone else: a lease only counts as yours when you have said who you are.

The policy, applied by every task that chooses devices:

  • Auto-selection skips claimed devices and prints each one it skipped and who holds it (exclude_claimed/2). That covers single-device auto-selection and the broad --all-devices / --all-physical scopes (MobDev.TaskTargets), mix mob.connect without --device, mix mob.push / mix mob.watch (MobDev.HotPush.connect/1) and the battery benches' device auto-detection.
  • A device named explicitly is used anyway, with a loud warning (warn_claimed/2). Naming the device is the same consent --device already gives for a phone; refusing would leave no way to reach a device whose lease outlived its owner short of releasing someone else's claim.

An iPhone found over the LAN (MobDev.Discovery.IOS.find_physical_at/1) carries its IP as its serial: EPMD names the app's node, not the phone, so there is no hardware UDID to match against a claim. Such an iPhone counts as claimed whenever another session claims a device that could be a physical iPhone (an iOS claim, or one of unknown platform, that is not a simulator). That can skip a free phone, which --device <ip> still reaches; the alternative deploys onto a leased one.

Summary

Functions

"claimed by agent-device session \"name\" (in /workspace)".

Auto-selection's filter: drop every device another session has claimed, printing one line per skipped device. Returns the devices left.

The claim another session holds on device, or nil when it is free or claimed by this session.

Leases from agent-device device status --json output a caller already ran, with the same warning as load/0 when it can't be read.

Read the current claims, and this process's session from AGENT_DEVICE_SESSION.

Split devices into the ones auto-selection may use and the ones another session has claimed, each paired with its claim. Order is preserved.

For devices the user named: warn loudly about each one another session has claimed, and return the list unchanged.

Types

claim()

@type claim() :: %{
  id: String.t(),
  platform: :android | :ios | nil,
  kind: :device | :emulator | :simulator | nil,
  session: String.t() | nil,
  workspace: String.t() | nil
}

t()

@type t() :: %MobDev.DeviceLeases{claims: [claim()], session: String.t() | nil}

Functions

describe(map)

@spec describe(claim()) :: String.t()

"claimed by agent-device session \"name\" (in /workspace)".

exclude_claimed(devices, leases, hint \\ "Name it with --device to use it anyway.")

@spec exclude_claimed([MobDev.Device.t()], t(), String.t()) :: [MobDev.Device.t()]

Auto-selection's filter: drop every device another session has claimed, printing one line per skipped device. Returns the devices left.

hint ends each line; the default points at --device, for tasks that have one.

foreign_claim(device, device_leases)

@spec foreign_claim(MobDev.Device.t(), t()) :: claim() | nil

The claim another session holds on device, or nil when it is free or claimed by this session.

from_status(output)

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

Leases from agent-device device status --json output a caller already ran, with the same warning as load/0 when it can't be read.

load()

@spec load() :: t()

Read the current claims, and this process's session from AGENT_DEVICE_SESSION.

Returns no claims when agent-device is not on PATH. When it is installed but its output can't be read, warns that leases are not being honoured and returns no claims, rather than blocking every deploy on a broken tool.

partition(devices, leases)

@spec partition([MobDev.Device.t()], t()) ::
  {[MobDev.Device.t()], [{MobDev.Device.t(), claim()}]}

Split devices into the ones auto-selection may use and the ones another session has claimed, each paired with its claim. Order is preserved.

warn_claimed(devices, leases)

@spec warn_claimed([MobDev.Device.t()], t()) :: [MobDev.Device.t()]

For devices the user named: warn loudly about each one another session has claimed, and return the list unchanged.