# Run and debug

In a project declaring `android`, `elide run` builds the application, installs it, launches it and prints its log
until you interrupt it or the app exits.

```bash
elide run                          # the one application the project declares
elide run app                      # or the one named; in a workspace, also `<member>:app`
elide run --device=emulator-5554   # on that device
elide run --emulator               # on the project's emulator, booted if needed
elide run --debugger=5005          # launched suspended, JDWP on port 5005
elide run --watch                  # again on every source change
```

| Command | Build target |
| --- | --- |
| `elide run [app]` | `android-run-<app>` |
| `elide run --device=<serial>` | `android-run-<app> --device=<serial>` |
| `elide run --emulator` | `android-run-emulator-<app>` |
| `elide run --debugger[=<port>]` | `android-debug-<app> [--debug=<port>]` |
| `elide run --debugger --emulator` | `android-debug-<app> --emulator` |

`elide build <target>` runs the same thing ([Android build targets](/docs/toolchain/android/targets)). Arguments after `--` are
refused: an app is started by its activity, not a command line.

The application is the one signed APK the project declares; with several, name one. A release APK with no `signing`
is published unsigned and cannot be installed, so it has no run target ([Signing and release](/docs/toolchain/android/release)).

## Choosing a device

| Flag | Device |
| --- | --- |
| `--device=<serial>` | That attached device or emulator; fails if it is not attached. |
| none | The one attached device, else the project's emulator, booted if needed. |
| `--emulator` | The project's emulator, booted or reused. |

More than one attached device is an error:

```text
More than one device is attached: emulator-5554, emulator-5556
Choose one with --device=<serial>
```

`adb devices` and `elide project info` list serials. `--emulator` uses only an emulator running the project's own
AVD, never an unrelated one that happens to be running.

## The log

After launching, the run follows the app's log (`adb logcat --pid=<pid>`) as the program's output. An app that exits
ends the run successfully; a device that goes away fails it. `elide build android-run-app --no-follow` returns as soon
as the app has launched, for scripts that only need it started.

## Rebuild on save

`elide run --watch` watches `elide.pkl` and every top-level directory of the project except hidden ones, `build`,
`target` and `node_modules`. A change stops the running app's session (its log and any debug forward), rebuilds,
reinstalls and relaunches; a failed build waits for the next change.

## Debug

```bash
elide run --debugger=5005
```

```text
Listening for transport dt_socket at address: 5005
```

The app is started suspended at its first instruction, its JDWP socket is forwarded to the local port, and the line
a JVM's debug agent prints is printed, which is what an editor's attach flow waits for. `--debugger` with no value
takes any free port. Attach a Java debugger to `localhost:<port>`, as to a JVM started with `suspend=y`:

```json
{
  "type": "java",
  "request": "attach",
  "name": "Attach to Android app",
  "hostName": "localhost",
  "port": 5005
}
```

On API 34 and later the app stays suspended until the debugger resumes it, so breakpoints set on attach are hit
from `onCreate`. Older devices run on once the debugger has been idle briefly. The session lasts until it is
interrupted or the app exits, and the port forward is removed on the way out.

Only a debuggable APK can be debugged. A debug build (`release` unset or `false`) is debuggable unless its manifest declares
`android:debuggable="false"`; a release artifact is debuggable only when its manifest says
`android:debuggable="true"`. An APK nothing can attach to is reported before a device is chosen.

`--debugger` takes a port (`5005`, `localhost:5005`) or nothing. Guest debugger modes (`cdp`, `dap`) and other hosts
are refused, since the forward binds loopback. JVM and script debugging is covered in [Debugging](/docs/debugging).

## The project's emulator

`android.device` describes the virtual device the project runs on:

```pkl
android {
  namespace = "com.example.app"
  compileSdk = 36
  device {
    avd = "my-app"
    api = 35
    deviceProfile = "pixel_6"
  }
}
```

| Property | Default |
| --- | --- |
| `avd` | The project's name. |
| `api` | `compileSdk`. Ignored when `systemImage` is set. |
| `systemImage` | An installed image for `api` on the host ABI, else `system-images;android-<api>;default;<abi>`. |
| `abi` | The host's: `arm64-v8a` on Apple silicon and ARM Linux, `x86_64` elsewhere. |
| `deviceProfile` | `pixel_6`, as `avdmanager list device` names profiles. |

The AVD is created on first use, so a machine that has never run an emulator goes through create, boot, install and
launch in one `elide run`. A missing system image is reported, and installed with `--install-sdk` or after the
license question on a terminal.

```bash
elide build android-emulator               # boot the AVD and wait until it can install
elide build android-emulator --no-window   # headless
elide build android-emulator-stop          # stop the emulator running the project's AVD
elide build android-avd                    # create or repair the AVD without booting it
```

The emulator outlives the build. It counts as booted once the device reports `sys.boot_completed` and its package
manager responds; the wait is bounded at five minutes, and a failed boot names the emulator's log under
`.dev/android/`. `android-emulator-stop` stops only emulators running the project's AVD, or the one `--device` names.

Before booting, Elide repairs an AVD the command-line tools created with settings Android Studio would not use: the
host GPU and keyboard turned off, or a platform target that keeps the emulator from using hardware acceleration.
A setting chosen in Android Studio is kept.

## Headless hosts, CI and WSL

The emulator shows its window only when Elide's output is a terminal, `CI` is unset and, on Linux, `DISPLAY` or
`WAYLAND_DISPLAY` is set. Elsewhere it boots headless (`-no-window -no-audio -gpu swiftshader_indirect`).
`--no-window` and `--window` decide explicitly.

### Hardware acceleration

Before an emulator starts, Elide asks it whether hardware acceleration works, and fails at once if it does not:

```text
The emulator cannot use hardware acceleration: This user doesn't have permissions to use KVM (/dev/kvm).
Add your user to the kvm group (sudo gpasswd -a $USER kvm) and log in again; until then, newgrp kvm or sg kvm -c "<command>" applies it to one shell
```

On Linux CI, `/dev/kvm` must exist and be usable by the build user. GitHub's Linux runners need a udev rule:

```yaml
- name: Enable KVM
  run: |
    echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
    sudo udevadm control --reload-rules
    sudo udevadm trigger --name-match=kvm
```

### The adb server

Every `adb` command Elide runs starts the `adb` server first, within 30 seconds, with `ADB_EMU=0` unless `ADB_EMU`
is set. A server started without it probes the emulator ports one at a time; where a connection to a closed
loopback port is dropped rather than refused (WSL2 with `networkingMode=mirrored`, some host firewalls) that scan
takes over half an hour. Elide registers running emulators with its server itself.

- A server already stuck scanning, started by hand, is cleared with `adb kill-server`.
- Run your own commands as `ADB_EMU=0 adb …` on such a host.
- A server you start by hand with `ADB_EMU=0` does not find emulators already running; restart the emulator, or
  stop the server and let Elide start it.

## Agents

`android/run` in `elide mcp` runs the same target, returns once the app has launched, and reports the device,
package, activity, whether the app is still running, and the end of its log ([Editors and agents](/docs/toolchain/android/editors#agents)).
