angel-android: Run AI agents on Android with Android shell access (Shizuku), a PRoot Linux terminal, and MCP tool support.
C++
2
2 commits
updated Oct 8, 2026
Native Kotlin/Compose app for the Pixel 10 Pro Fold (works on any ARM64 Android 8+). Android Studio owns run configuration; terminal builds use the Gradle wrapper. No root, su, or bootloader changes: privileged actions go through Shizuku as the ADB shell user (uid 2000).
android_shell – one command on the phone as uid 2000 (dumpsys, pm/cmd, am, settings, input tap/swipe/text…).read_screen – compact list of on-screen UI elements with tap coordinates (uiautomator dump), so the agent can see an app, tap, and verify.linux_shell – one command in the selected Linux distro (Alpine, Debian, Ubuntu or Kali, ARM64 under proot), files persisted in /root, phone storage at /sdcard.node_modules, Python into a venv under /root/.angel/mcp/<id>, missing Node/Python added via apt/apk), starts it over stdio and offers its tools to the model as mcp__<server>__<tool>. Browse lists a hand-checked Featured set (Fetch, DuckDuckGo, Filesystem, Memory, Sequential Thinking, Time, Context7) plus search of the official MCP Registry (community npm/PyPI servers, shown with a third-party-code warning). Connect to hosted server takes a hostname/IP (Streamable HTTP or legacy SSE, optional bearer token/headers); plain http:// is accepted only for private addresses (LAN, localhost, Tailscale, .local), everything else needs https://. Custom command runs any stdio server in Linux or the Android shell. Each server has its own Ask/Allow/Block policy, per-tool switches, a log, and encrypted credentials; calls obey the same approval sheet (arguments are editable) and the per-command Kill button. Servers keep running in the distro they were installed into.Settings → On-device → Manage local models.
owner/repo), pick a single-file GGUF quantization. Each file shows its size and whether it fits this phone's RAM. Downloads run in Android's DownloadManager (background, notification, Wi-Fi only unless allowed) and are checked against Hugging Face's published SHA-256 and the GGUF header before use. A Hugging Face token (optional, encrypted) unlocks gated models; it is only sent to huggingface.co, never to the download CDN.app/src/main/cpp/llama.cpp, see VENDORED.md), CPU-only, with runtime selection of the best ARM kernels (ARMv8.0 → ARMv9.2 variants: dotprod, i8mm, SVE2). Each model's own chat template is used, including its native tool-call format, so local models can use android_shell, linux_shell and read_screen like cloud models. Replies stream; the prompt cache is reused between turns.connectedDebugAndroidTest uninstalled the app after the test run, wiping the encrypted keys. gradle.properties now sets android.injected.androidTest.leaveApksInstalledAfterRun=true, and the recommended test command below never uninstalls. The provider choice is now persisted as soon as you pick it, and unsaved edits no longer vanish on fold/unfold.deepseek-chat/deepseek-reasoner were discontinued by DeepSeek on 2026-07-24. Saved DeepSeek profiles using them are migrated to deepseek-flash automatically. Errors now name the provider and include its reason (API key redacted), and assistant messages are sent back with only the fields each API accepts.Connected: uid 2000 (ADB shell).API keys must be entered on the device, never in source code or chat with a coding assistant.
| File | Role |
|---|---|
ui/theme/ | AngelTheme: color schemes (dark/light), AngelColors for status/terminal colors, typography, shapes |
ui/components/Components.kt | Shared pieces: SectionCard, InfoRow, HealthDot, Pill, Callout, EmptyState |
ui/AngelApp.kt | Screen switching with transitions, snackbar notices, approval sheet |
ui/chat/ChatScreen.kt | Header, empty state with setup checklist, message list, composer |
ui/chat/ToolActivityCard.kt, ToolDetailSheet.kt, ApprovalSheet.kt, ToolCalls.kt | Tool-call grouping, detail sheet, approval sheet, status mapping |
ui/chat/Markdown.kt | Lightweight Markdown renderer (prose, code, tables) |
ui/mcp/ | MCP screen (Installed / Browse), install sheet, hosted-server and custom-server dialogs |
mcp/ | Server catalog and registry client, install recipes, stdio / Streamable HTTP / SSE transports, JSON-RPC client, McpManager lifecycle and tool bridge |
ui/SettingsScreen.kt, ui/HistoryScreen.kt, ui/terminal/, ui/models/ | Settings cards, grouped conversation history with search, terminal, on-device models |
The launch window theme (res/values/themes.xml, values-night/) matches the Compose background so there is no flash on start. dev.sh rebuilds and redeploys on save.
export JAVA_HOME=/home/puto/codebase/android-studio/jbr
export PATH="$HOME/Android/Sdk/platform-tools:$PATH" # the SDK adb, not a distro adb
./gradlew installDebug
adb shell am start -n com.bruh.angel/.MainActivity
./gradlew testDebugUnitTest lintDebug # JVM unit tests + lint
./gradlew installDebug installDebugAndroidTest # device tests without uninstalling anything:
adb shell am instrument -w -r com.bruh.angel.test/androidx.test.runner.AndroidJUnitRunner
Device tests need Shizuku running with permission granted to Angel and Linux installed. They never call paid APIs (provider tests use in-memory responses).
| Suite | Covers |
|---|---|
ProviderContractTest | All four adapters, tool round trips, sanitized assistant history, DeepSeek reasoning passback, Gemini thought signatures, read_screen, model listing, readable-but-redacted errors |
StorageSecurityTest | Encrypted profiles, persisted selection, retired-model migration, agent prefs, conversations, screen-dump summarizing, archive traversal/link rejection, symlink-safe cleanup |
ShizukuIntegrationTest | uid 2000, Linux execution and /sdcard, cancellation, corrupt-download rejection, Android and Linux terminal sessions incl. Ctrl-C, reading the screen |
LocalModelTest | Hugging Face listing parsing; real download (DownloadManager + SHA-256) of SmolLM2-135M, JNI load, streamed generation, UTF-8, prompt-cache reuse, output parsing. Opt-in -e realModel true: Qwen3-0.6B end-to-end tool call (android_shell) and answer |
| Unit tests | Terminal buffer (ANSI stripping, CR/backspace, scrollback), Markdown block parsing, provider sanity checks |
Exact HTTPS URLs and SHA-256 pins are in linuxenv/LinuxDistro.kt (root filesystems: Alpine 3.23.6 minirootfs from dl-cdn.alpinelinux.org, Debian 13 slim from the official debuerreotype artifacts, Ubuntu Base 26.04.1 from cdimage.ubuntu.com, Kali 2026.2 NetHunter minimal from kali.download, whose hash matches Kali's published SHA256SUMS) and linuxenv/LinuxArtifacts.kt (Termux ARM64 proot 5.1.107.96, libtalloc 2.5.0, libandroid-shmem 0.7 (packages.termux.dev). Hashes are verified before extraction; archives with unsafe paths or links are rejected; activation is smoke-tested and rolled back on failure. The environment lives in /data/local/tmp/angel-linux (owned by uid 2000, mode 0700) and can survive app uninstall. Proot is not a security sandbox.
read_screen uses the accessibility tree; apps that draw their own UI (games, some video/maps views) expose little. No screenshot/vision input yet.angel-android: Run AI agents on Android with Android shell access (Shizuku), a PRoot Linux terminal, and MCP tool support.
C++
2
2 commits
updated Oct 8, 2026
Native Kotlin/Compose app for the Pixel 10 Pro Fold (works on any ARM64 Android 8+). Android Studio owns run configuration; terminal builds use the Gradle wrapper. No root, su, or bootloader changes: privileged actions go through Shizuku as the ADB shell user (uid 2000).
android_shell – one command on the phone as uid 2000 (dumpsys, pm/cmd, am, settings, input tap/swipe/text…).read_screen – compact list of on-screen UI elements with tap coordinates (uiautomator dump), so the agent can see an app, tap, and verify.linux_shell – one command in the selected Linux distro (Alpine, Debian, Ubuntu or Kali, ARM64 under proot), files persisted in /root, phone storage at /sdcard.node_modules, Python into a venv under /root/.angel/mcp/<id>, missing Node/Python added via apt/apk), starts it over stdio and offers its tools to the model as mcp__<server>__<tool>. Browse lists a hand-checked Featured set (Fetch, DuckDuckGo, Filesystem, Memory, Sequential Thinking, Time, Context7) plus search of the official MCP Registry (community npm/PyPI servers, shown with a third-party-code warning). Connect to hosted server takes a hostname/IP (Streamable HTTP or legacy SSE, optional bearer token/headers); plain http:// is accepted only for private addresses (LAN, localhost, Tailscale, .local), everything else needs https://. Custom command runs any stdio server in Linux or the Android shell. Each server has its own Ask/Allow/Block policy, per-tool switches, a log, and encrypted credentials; calls obey the same approval sheet (arguments are editable) and the per-command Kill button. Servers keep running in the distro they were installed into.Settings → On-device → Manage local models.
owner/repo), pick a single-file GGUF quantization. Each file shows its size and whether it fits this phone's RAM. Downloads run in Android's DownloadManager (background, notification, Wi-Fi only unless allowed) and are checked against Hugging Face's published SHA-256 and the GGUF header before use. A Hugging Face token (optional, encrypted) unlocks gated models; it is only sent to huggingface.co, never to the download CDN.app/src/main/cpp/llama.cpp, see VENDORED.md), CPU-only, with runtime selection of the best ARM kernels (ARMv8.0 → ARMv9.2 variants: dotprod, i8mm, SVE2). Each model's own chat template is used, including its native tool-call format, so local models can use android_shell, linux_shell and read_screen like cloud models. Replies stream; the prompt cache is reused between turns.connectedDebugAndroidTest uninstalled the app after the test run, wiping the encrypted keys. gradle.properties now sets android.injected.androidTest.leaveApksInstalledAfterRun=true, and the recommended test command below never uninstalls. The provider choice is now persisted as soon as you pick it, and unsaved edits no longer vanish on fold/unfold.deepseek-chat/deepseek-reasoner were discontinued by DeepSeek on 2026-07-24. Saved DeepSeek profiles using them are migrated to deepseek-flash automatically. Errors now name the provider and include its reason (API key redacted), and assistant messages are sent back with only the fields each API accepts.Connected: uid 2000 (ADB shell).API keys must be entered on the device, never in source code or chat with a coding assistant.
| File | Role |
|---|---|
ui/theme/ | AngelTheme: color schemes (dark/light), AngelColors for status/terminal colors, typography, shapes |
ui/components/Components.kt | Shared pieces: SectionCard, InfoRow, HealthDot, Pill, Callout, EmptyState |
ui/AngelApp.kt | Screen switching with transitions, snackbar notices, approval sheet |
ui/chat/ChatScreen.kt | Header, empty state with setup checklist, message list, composer |
ui/chat/ToolActivityCard.kt, ToolDetailSheet.kt, ApprovalSheet.kt, ToolCalls.kt | Tool-call grouping, detail sheet, approval sheet, status mapping |
ui/chat/Markdown.kt | Lightweight Markdown renderer (prose, code, tables) |
ui/mcp/ | MCP screen (Installed / Browse), install sheet, hosted-server and custom-server dialogs |
mcp/ | Server catalog and registry client, install recipes, stdio / Streamable HTTP / SSE transports, JSON-RPC client, McpManager lifecycle and tool bridge |
ui/SettingsScreen.kt, ui/HistoryScreen.kt, ui/terminal/, ui/models/ | Settings cards, grouped conversation history with search, terminal, on-device models |
The launch window theme (res/values/themes.xml, values-night/) matches the Compose background so there is no flash on start. dev.sh rebuilds and redeploys on save.
export JAVA_HOME=/home/puto/codebase/android-studio/jbr
export PATH="$HOME/Android/Sdk/platform-tools:$PATH" # the SDK adb, not a distro adb
./gradlew installDebug
adb shell am start -n com.bruh.angel/.MainActivity
./gradlew testDebugUnitTest lintDebug # JVM unit tests + lint
./gradlew installDebug installDebugAndroidTest # device tests without uninstalling anything:
adb shell am instrument -w -r com.bruh.angel.test/androidx.test.runner.AndroidJUnitRunner
Device tests need Shizuku running with permission granted to Angel and Linux installed. They never call paid APIs (provider tests use in-memory responses).
| Suite | Covers |
|---|---|
ProviderContractTest | All four adapters, tool round trips, sanitized assistant history, DeepSeek reasoning passback, Gemini thought signatures, read_screen, model listing, readable-but-redacted errors |
StorageSecurityTest | Encrypted profiles, persisted selection, retired-model migration, agent prefs, conversations, screen-dump summarizing, archive traversal/link rejection, symlink-safe cleanup |
ShizukuIntegrationTest | uid 2000, Linux execution and /sdcard, cancellation, corrupt-download rejection, Android and Linux terminal sessions incl. Ctrl-C, reading the screen |
LocalModelTest | Hugging Face listing parsing; real download (DownloadManager + SHA-256) of SmolLM2-135M, JNI load, streamed generation, UTF-8, prompt-cache reuse, output parsing. Opt-in -e realModel true: Qwen3-0.6B end-to-end tool call (android_shell) and answer |
| Unit tests | Terminal buffer (ANSI stripping, CR/backspace, scrollback), Markdown block parsing, provider sanity checks |
Exact HTTPS URLs and SHA-256 pins are in linuxenv/LinuxDistro.kt (root filesystems: Alpine 3.23.6 minirootfs from dl-cdn.alpinelinux.org, Debian 13 slim from the official debuerreotype artifacts, Ubuntu Base 26.04.1 from cdimage.ubuntu.com, Kali 2026.2 NetHunter minimal from kali.download, whose hash matches Kali's published SHA256SUMS) and linuxenv/LinuxArtifacts.kt (Termux ARM64 proot 5.1.107.96, libtalloc 2.5.0, libandroid-shmem 0.7 (packages.termux.dev). Hashes are verified before extraction; archives with unsafe paths or links are rejected; activation is smoke-tested and rolled back on failure. The environment lives in /data/local/tmp/angel-linux (owned by uid 2000, mode 0700) and can survive app uninstall. Proot is not a security sandbox.
read_screen uses the accessibility tree; apps that draw their own UI (games, some video/maps views) expose little. No screenshot/vision input yet.