# `MobDev.DeviceLeases`
[🔗](https://github.com/genericjam/mob_dev/blob/master/lib/mob_dev/device_leases.ex#L1)

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.

# `claim`

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

# `t`

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

# `describe`

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

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

# `exclude_claimed`

```elixir
@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`

```elixir
@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`

```elixir
@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`

```elixir
@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`

```elixir
@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`

```elixir
@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.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
