Current section
Files
Jump to
Current section
Files
lib/ex_mobile_device/muxd.ex
defmodule ExMobileDevice.Muxd do
@moduledoc """
Device management through `usbmuxd`.
This module provides support for:
- Listing the currently connected devices
- Subscribing for device arrival/departure
- Fetching the pairing record of a specified device
- Starting, and connecting to, various device services
"""
alias ExMobileDevice.Muxd.Connection
alias ExMobileDevice.Muxd.ConnectionSupervisor
alias ExMobileDevice.Muxd.Monitor
@doc """
List the currently connected devices.
## Example
iex(1)> ExMobileDevice.Muxd.list_devices()
[]
iex(2)> # Plug a device in!
nil
iex(3)> ExMobileDevice.Muxd.list_devices()
["00008120-0018DEADC0DEFACE"]
"""
@spec list_devices() :: list(String.t())
def list_devices do
Monitor.list_devices()
end
@doc """
Subscribe for device arrival and departure.
On success, this function returns the list of devices present at the
time of subscription. Any device notifications received subsequently
are guaranteed to have happened after this snapshot.
The subscription is automatically removed when the subscriber exits.
> #### Multiple subscriptions {: .info}
>
> Multiple subscriptions from the same process will result in duplicate
> events being delivered to that process
Device events are sent to the subscribing process and have the form:
```elixir
{:exmobiledevice, {:device_attached, device_id}} # device arrivals
{:exmobiledevice, {:device_detached, device_id}} # device departures
```
Additionally, if the connection to `usbmuxd` itself is lost (or recovered),
the subscribing process will receive the following messages:
```elixir
{:exmobiledevice, :disconnected} # On disconnection
{:exmobiledevice, :connected} # On reconnection
{:exmobiledevice, {:device_attached, device_id}} # For each connected device, on reconnection
```
## Example
iex(1)> ExMobileDevice.Muxd.subscribe()
{:ok, []}
iex(2)> # Plug a device in!
nil
iex(3)> flush
{:exmobiledevice, {:device_attached, "00008120-0018DEADC0DEFACE"}}
:ok
iex(4)> # Unplug the device!
nil
iex(5)> flush
{:exmobiledevice, {:device_detached, "00008120-0018DEADC0DEFACE"}}
:ok
"""
@spec subscribe() :: {:ok, list(String.t())} | {:error, any()}
def subscribe do
Monitor.subscribe()
end
@doc """
Connect to `usbmuxd`.
On success, a process is returned that encapsulates the connection and
monitors the caller of this function, exiting if the caller exits.
This process may be used to:
- Retrieve pairing records from `usbmuxd`. See `get_pair_record/2`.
- Connect to other services on the device. See `connect_thru/3`.
## Example
iex(1)> ExMobileDevice.Muxd.connect()
{:ok, #PID<0.212.0>}
"""
@spec connect() :: DynamicSupervisor.on_start_child()
def connect do
ConnectionSupervisor.start_connection(controlling_process: self())
end
@doc """
Retrieve the pairing record for the specified device.
## Example
iex(1)> {:ok, pid} = ExMobileDevice.Muxd.connect()
{:ok, #PID<0.199.0>}
iex(2)> ExMobileDevice.Muxd.get_pair_record(pid, "00008120-0018DEADC0DEFACE")
{:ok,
%{
"DeviceCertificate" => "-----BEGIN CERTIFICATE----- ... elided ..."
}}
"""
@spec get_pair_record(:gen_statem.server_ref(), String.t()) :: {:ok, map()} | {:error, any}
def get_pair_record(conn, udid) do
Connection.get_pair_record(conn, udid)
end
@doc """
Connect to the specified port on the specified device.
On success, the connected socket is returned to the caller, and the caller becomes
its [controlling process](https://www.erlang.org/doc/man/gen_tcp#controlling_process-2).
> #### Conn shutdown {: .info}
>
> Once the underlying socket has been connected through to the specific device port
> and returned to the caller, it can no longer be used to communicate with `usdbmuxd`
> again and the process represented by `conn` will automatically stop after returning.
## Example
iex(1)> {:ok, conn} = ExMobileDevice.Muxd.connect()
{:ok, #PID<0.200.0>}
iex(2)> {:ok, sock} = ExMobileDevice.Muxd.connect_thru(conn, "00008120-0018DEADC0DEFACE", 62078) # lockdownd
{:ok, #Port<0.4>}
iex(3)> Process.alive?(conn)
false
"""
@spec connect_thru(:gen_statem.server_ref(), String.t(), non_neg_integer()) ::
{:ok, port()} | {:error, any()}
def connect_thru(conn, udid, port) do
Connection.connect_thru(conn, udid, port)
end
@doc """
Returns the _platform-dependent_ 'location id' of the specified
device, or `nil` if no such device is attached.
On Linux, the 32-bit integer returned is composed of the USB
bus number (high 16 bits) and USB device number (low 16 bits).
On OSX, the integer can be used to form a name that can be used
to find the device vis the IORegistry.
This function can be useful when locating the device in the host's
device tree.
## Example
iex(1)> ExMobileDevice.Muxd.get_location_id("00008120-0018DEADC0DEFACE")
1048576
"""
@spec get_location_id(String.t()) :: integer() | nil
def get_location_id(udid) do
Monitor.get_location_id(udid)
end
end