@oahl/adapter-android
v0.1.5
Published
OAHL adapter for Android devices — exposes screen, input, app management, file transfer, system info, and logcat over ADB.
Maintainers
Readme
@oahl/adapter-android
An OAHL adapter for Android devices. Exposes screen capture, input simulation, app management, file transfer, system info, and logcat over ADB — making Android devices available as hardware capabilities to AI agents.
Prerequisites
- Node.js >= 18
- Android Platform Tools (
adb) installed and on your PATH - An Android device with USB Debugging enabled (Settings → Developer Options → USB Debugging), or a running Android emulator
Verify your setup:
adb devicesYou should see your device listed with state device.
Installation
npm install @oahl/adapter-androidUsage
Register in oahl-config.json
{
"adapters": [
{
"id": "android-adapter",
"module": "@oahl/adapter-android"
}
]
}Then start your node:
oahl startUse directly in code
import AndroidAdapter from '@oahl/adapter-android';
const adapter = new AndroidAdapter();
await adapter.initialize();
const devices = await adapter.getDevices();
console.log(devices);
// [{ id: 'emulator-5554', type: 'android', name: 'Google Pixel 7 (Android 14, API 34)', isPublic: false }]
const caps = await adapter.getCapabilities(devices[0].id);
// Take a screenshot
const result = await adapter.execute(devices[0].id, 'screen.screenshot', { format: 'png' });
console.log(result.localPath); // /tmp/oahl-android-screenshots/screenshot_1234567890.pngConstructor options
new AndroidAdapter({
screenshotDir: '/your/custom/output/dir' // default: /tmp/oahl-android-screenshots
})Capabilities
All capabilities use JSON Schema-validated arguments. Device id is the ADB serial (from adb devices).
screen.screenshot
Capture a screenshot of the device screen.
| Argument | Type | Default | Description |
|---|---|---|---|
| format | "png" \| "jpg" | "png" | Output image format |
| outputPath | string | auto | Local path to save the file |
await adapter.execute(deviceId, 'screen.screenshot', { format: 'png' });
// → { localPath, format, timestamp }screen.record
Record the device screen for a set duration.
| Argument | Type | Default | Description |
|---|---|---|---|
| durationSeconds | integer | 10 | Duration (1–180s) |
| bitrateMbps | number | 4 | Video bitrate in Mbps (0.5–20) |
| outputPath | string | auto | Local path to save the .mp4 |
await adapter.execute(deviceId, 'screen.record', { durationSeconds: 15 });
// → { localPath, durationSeconds, timestamp }input.tap
Simulate a tap at screen coordinates.
| Argument | Type | Required | Description |
|---|---|---|---|
| x | integer | ✓ | X coordinate in pixels |
| y | integer | ✓ | Y coordinate in pixels |
await adapter.execute(deviceId, 'input.tap', { x: 540, y: 960 });input.swipe
Simulate a swipe gesture between two points.
| Argument | Type | Required | Description |
|---|---|---|---|
| x1, y1 | integer | ✓ | Start coordinates |
| x2, y2 | integer | ✓ | End coordinates |
| durationMs | integer | — | Swipe duration (50–5000ms, default 300) |
// Scroll down
await adapter.execute(deviceId, 'input.swipe', { x1: 540, y1: 1200, x2: 540, y2: 400 });input.text
Type text into the currently focused input field.
| Argument | Type | Required | Description |
|---|---|---|---|
| text | string | ✓ | Text to type (max 1000 chars) |
await adapter.execute(deviceId, 'input.text', { text: 'hello world' });input.keyevent
Send a hardware key event.
| Argument | Type | Required | Description |
|---|---|---|---|
| keycode | string | ✓ | Keycode name or integer, e.g. "KEYCODE_HOME", "4" |
await adapter.execute(deviceId, 'input.keyevent', { keycode: 'KEYCODE_BACK' });Common keycodes: KEYCODE_HOME (3), KEYCODE_BACK (4), KEYCODE_VOLUME_UP (24), KEYCODE_POWER (26).
app.launch
Launch an app by package name.
| Argument | Type | Required | Description |
|---|---|---|---|
| package | string | ✓ | Package name, e.g. "com.android.settings" |
| activity | string | — | Fully-qualified activity (optional) |
await adapter.execute(deviceId, 'app.launch', { package: 'com.android.chrome' });app.stop
Force-stop a running app.
await adapter.execute(deviceId, 'app.stop', { package: 'com.android.chrome' });app.install
Install an APK from the host machine.
| Argument | Type | Required | Description |
|---|---|---|---|
| apkPath | string | ✓ | Absolute path to the .apk on the host |
| replaceExisting | boolean | — | Reinstall if already installed (default true) |
await adapter.execute(deviceId, 'app.install', { apkPath: '/home/user/myapp.apk' });app.uninstall
Uninstall an app.
| Argument | Type | Required | Description |
|---|---|---|---|
| package | string | ✓ | Package to uninstall |
| keepData | boolean | — | Preserve app data (default false) |
app.list
List installed packages.
| Argument | Type | Default | Description |
|---|---|---|---|
| filter | "all" \| "system" \| "third-party" \| "enabled" \| "disabled" | "all" | Which packages to return |
const { packages } = await adapter.execute(deviceId, 'app.list', { filter: 'third-party' });file.push
Push a file from the host to the device.
await adapter.execute(deviceId, 'file.push', {
localPath: '/home/user/data.csv',
remotePath: '/sdcard/Download/data.csv'
});file.pull
Pull a file from the device to the host.
await adapter.execute(deviceId, 'file.pull', {
remotePath: '/sdcard/DCIM/photo.jpg',
localPath: '/home/user/photo.jpg'
});system.info
Return system information. Sections default to ["battery", "network", "storage"].
| Section | Returns |
|---|---|
| battery | Level (%), status, plugged state |
| network | WiFi info, IP routes |
| storage | df output for /sdcard |
| cpu | Hardware and processor info |
| memory | /proc/meminfo top entries |
| display | Screen resolution and density |
const info = await adapter.execute(deviceId, 'system.info', {
sections: ['battery', 'display']
});system.shell
Run a raw ADB shell command.
const { output } = await adapter.execute(deviceId, 'system.shell', {
command: 'ls /sdcard'
});⚠️ No sandboxing is applied. Use OAHL policies to restrict access to this capability in production.
logcat.dump
Dump recent logcat output.
| Argument | Type | Default | Description |
|---|---|---|---|
| lines | integer | 200 | Number of recent lines (1–5000) |
| filter | string | "*:V" | Logcat filter, e.g. "*:E" for errors only |
const { log } = await adapter.execute(deviceId, 'logcat.dump', {
lines: 100,
filter: '*:E'
});Build
npm install
npm run build # outputs to dist/License
Apache-2.0
