# `DalaDev.Discovery.Android`
[🔗](https://github.com/manhvu/dala_dev/blob/main/lib/dala_dev/discovery/android.ex#L1)

Discovers Android devices and emulators via adb.

# `developer_mode`

```elixir
@spec developer_mode(String.t()) :: :enabled | :disabled | :unknown
```

Check if developer mode is enabled on the device.
Returns :enabled | :disabled | :unknown.

# `device_node_suffix`

```elixir
@spec device_node_suffix(String.t()) :: String.t()
```

Returns the Dala node-name suffix for the device reachable via the given
adb identifier. The suffix is derived from the device's hardware serial
(`ro.serialno`), which is stable across USB and WiFi-adb identifiers for
the same physical phone — so a deploy that targets `ZY22K6BSJM` (USB)
and a bench that targets `10.0.0.17:5555` (WiFi) both end up using the
same node name.

Falls back to sanitizing the adb identifier itself when `getprop` fails
(e.g. unrooted device, missing executable, dead transport). The
fallback is the legacy behaviour, kept so a bench against a
pre-suffix-aware deploy still converges on *some* deterministic name.

Single adb shell call (~100–300 ms). Suitable for once-per-launch use
by the deployer and bench.

# `list_devices`

```elixir
@spec list_devices() :: [DalaDev.Device.t()]
```

Returns a list of %Device{} for all adb-visible Android devices.

# `node_suffix_for`

```elixir
@spec node_suffix_for(String.t()) :: String.t()
```

Sanitizes a string into a Dala node-name suffix. Pure — no adb calls.

    node_suffix_for("ZY22CRLMWK")        → "zy22crlmwk"
    node_suffix_for("10.0.0.82:5555")    → "10_0_0_82"
    node_suffix_for("emulator-5554")     → "emulator_5554"

Used as the final transformation step by `device_node_suffix/1` (which
asks the device for a stable hardware serial and runs it through here).
Tests use it directly to verify the sanitization rules.

# `parse_devices_output`

```elixir
@spec parse_devices_output(String.t()) :: [DalaDev.Device.t()]
```

Parses the raw output of `adb devices -l` into a list of `%Device{}`.
Does not perform enrichment (no adb calls for name/version).
Exposed for testing.

# `restart_app`

```elixir
@spec restart_app(String.t(), String.t(), String.t(), keyword()) ::
  {:ok, String.t()} | {:error, String.t()}
```

Restarts the app on the device, optionally passing a dist_port intent extra.

Runs `chcon` before `am start` to heal any SELinux MCS category mismatch on OTP
files. This mismatch happens when the APK is reinstalled and Android assigns a new
MCS category to the package — files pushed via `adb push` retain the old label and
the BEAM can't access them.

The label is copied from the app's `cache/` directory, not `files/`. On Android 15
the `files/` directory itself lacks MCS categories (`s0` only), whereas `cache/`
always carries the full `s0:cXXX,cYYY` label that installd assigns to the package.

The `chcon` requires root (`adb root`) — it's silently skipped on non-rooted devices
where the OTP files were pushed with the correct label to begin with.

---

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