Current section

Files

Jump to
exmobiledevice lib ex_mobile_device diagnostics.ex
Raw

lib/ex_mobile_device/diagnostics.ex

defmodule ExMobileDevice.Diagnostics do
@moduledoc """
Services for restarting, shutting down, or sleeping a device.
"""
alias ExMobileDevice.Diagnostics.RestartManager
alias ExMobileDevice.Services
alias ExMobileDevice.TaskSupervisor
require Logger
@diagnostics "com.apple.mobile.diagnostics_relay"
@default_tmo 30_000
@doc """
Restart the specified device.
"""
@spec restart(String.t(), timeout()) :: :ok | {:error, any}
def restart(udid, timeout \\ @default_tmo) do
run_in_task(udid, "Restart", timeout)
end
@doc """
Shutdown the specified device.
"""
@spec shutdown(String.t(), timeout()) :: :ok | {:error, any}
def shutdown(udid, timeout \\ @default_tmo) do
run_in_task(udid, "Shutdown", timeout)
end
@doc """
Send the specified device to sleep.
"""
@spec sleep(String.t(), timeout()) :: :ok | {:error, any}
def sleep(udid, timeout \\ @default_tmo) do
run_in_task(udid, "Sleep", timeout)
end
@doc false
def ioreg(udid, opts \\ []) do
args =
opts
|> Keyword.take([:current_plane, :entry_name, :entry_class])
|> Map.new(fn {k, v} -> {to_ioreg_arg(k), v} end)
timeout = Keyword.get(opts, :timeout, @default_tmo)
run_in_task(
fn ->
with {:ok, ssl_sock} <- Services.connect(udid, @diagnostics) do
case Services.rpc(ssl_sock, "IORegistry", args) do
{:ok, %{"Status" => "Success"} = response} ->
{:ok, get_in(response, ["Diagnostics", "IORegistry"])}
_ ->
{:error, :failed}
end
end
end,
timeout
)
end
defp to_ioreg_arg(:current_plane), do: "CurrentPlane"
defp to_ioreg_arg(:entry_class), do: "EntryClass"
defp to_ioreg_arg(:entry_name), do: "EntryName"
defp run_in_task(udid, request, timeout) when is_binary(request) do
run_in_task(
fn ->
with {:ok, ssl_sock} <- Services.connect(udid, @diagnostics, timeout),
{:ok, %{"Status" => "Success"}} <- Services.rpc(ssl_sock, request, %{}) do
:ok
else
error ->
Logger.error(
"#{__MODULE__}: #{request} request for #{udid} failed. #{inspect(error)}"
)
# Try with RestartManager
Logger.warning("Attempting #{request} request for #{udid} using RestartManager...")
RestartManager.request(udid, request, timeout)
end
end,
# Should never get here. Give the Services.connect call enough time to timeout
ceil(timeout * 1.1)
)
end
defp run_in_task(fun, timeout) when is_function(fun, 0) do
task = TaskSupervisor.async_nolink(fun)
case Task.yield(task, timeout) || Task.shutdown(task) do
{:ok, result} ->
result
nil ->
{:error, :timeout}
end
end
end