Android JavaScript SDK
@midscene/android controls Android devices over adb. AndroidAgent inherits all AI methods from the base Agent.
Imports and API overview
In most cases, use the agentFromAdbDevice() factory to create and connect a Device in one step. If you need to configure and manage the AndroidDevice and AndroidAgent instances separately, construct them explicitly.
agentFromAdbDevice()
Connects to an Android device and returns an AndroidAgent bound to the connected Device. deviceId is optional; when omitted, Midscene selects the first connected device and throws an error if none is found. options accepts both Agent and Device settings (including adb connection settings used during device discovery).
AndroidDevice / AndroidAgent
When constructing AndroidDevice explicitly, call await device.connect() before passing the Device to AndroidAgent. Each Device instance belongs to a single Agent.
Configuration options
Agent options
Accepts all shared Agent options, plus the following platform option:
appNameMapping?: Record<string, string>— Maps friendly app names to Android package names so you can reference apps by name inlaunch(),terminate(), or natural-language prompts. User-provided mappings take precedence over built-in defaults.
Device options
autoDismissKeyboard?: boolean— Whether to automatically dismiss the on-screen keyboard after text input. Default:true.keyboardDismissStrategy?: 'esc-first' | 'back-first'— Key priority used when dismissing the on-screen keyboard. Default:'esc-first'.keyboardTypeDelay?: number— Delay in milliseconds between keystrokes (must be a finite non-negative number). In'legacy'mode, a positive value causes the nativeadb input textpath to send characters one Unicode code point at a time, while the yadb path ignores this option for backward compatibility. To apply per-character delay to yadb input as well, setinputStrategyto'sequential'.inputStrategy?: 'legacy' | 'sequential' | 'bulk'— Default text input strategy forInputactions. Default:'legacy':'sequential': Makes oneadbor yadb call per Unicode code point. Use this strategy when setting a positivekeyboardTypeDelay.'bulk': Sends the entire string in a single backend call when supported by the active input method. RequireskeyboardTypeDelayto be omitted or set to0.
androidAdbPath?: string— Custom path to theadbexecutable.remoteAdbHost?: string/remoteAdbPort?: number— Host and port of a remoteadbserver.imeStrategy?: 'always-yadb' | 'yadb-for-non-ascii'— Controls when yadb is used for text input. Default:'yadb-for-non-ascii':'yadb-for-non-ascii'(default): Uses yadb for non-ASCII characters (including accented Latin characters such asö,é, andñ, as well as Chinese and Japanese text) and format specifiers (such as%sand%d). Pure ASCII text uses the faster nativeadb input text.'always-yadb': Uses yadb for all text input. Provides maximum compatibility, though slightly slower for pure ASCII text.
screenshotStrategy?: 'auto' | 'always-yadb'— Controls how screenshots are captured. Default:'auto':'auto'(default): Triesadb.takeScreenshotfirst, falls back to shellscreencap, and finally uses yadb ifscreencapfails (whenscrcpyConfig.enabledistrue, scrcpy is tried first). Note that'auto'does not inspect image contents—if a capture command succeeds but returns a black frame, it will not automatically fall back to the next method.'always-yadb': Bypasses the default'auto'flow and captures screenshots directly via yadb. Useful whenscreencapreturns black frames on secure screens (FLAG_SECURE) while yadb can capture them (actual support depends on the Android version, ROM, root/hook environment, and device setup; always verify on your target device). Yadb only supports the default display (displayId=0); combining'always-yadb'with a non-zerodisplayIdthrows an error. Can also be configured via theMIDSCENE_ANDROID_SCREENSHOT_STRATEGY=always-yadbenvironment variable.
displayId?: number— Target virtual display ID when the device has multiple displays.exposeRunAdbShellAction?: boolean— Whether to expose the built-inRunAdbShellaction in the action space. Default:true. Set tofalseto prevent the AI planner, YAML scripts, andagent.runAdbShell()from executingadb shellcommands.customActions?: DeviceAction[]— Custom actions registered for the Agent planner to invoke. Pass this option tonew AndroidDevice()oragentFromAdbDevice(); see Integrate with any interface for details.screenshotResizeScale?: number— Deprecated (removed and no longer has any effect). UsescreenshotShrinkFactorinAgentOptinstead to control the screenshot size sent to the AI model.minScreenshotBufferSize?: number— Minimum valid screenshot buffer size in bytes. Smaller buffers are rejected as failed or corrupted captures. Default:1024(1 KB). Set to0to skip this size check (empty buffers and invalid image formats are still rejected).alwaysRefreshScreenInfo?: boolean— Whether to re-query screen rotation and resolution before every step. Default:false.
scrcpy configuration and status methods
scrcpyConfig?: object— High-performance scrcpy screenshot configuration (disabled by default):enabled?: boolean— Whether to enable scrcpy screenshots. Default:false.maxSize?: number— Maximum width or height of captured screenshots in pixels (must be a non-negative integer; default:0, which disables scaling). When scrcpy is temporarily unavailable and falls back toadb/ yadb, this limit still applies so planning and report images do not suddenly revert to full device resolution.videoBitRate?: number— scrcpy H.264 encoding bitrate in bits per second. Default:100000000. Adjusting this trades encoding bandwidth against screenshot detail; tune it only when independent transport measurements indicate a need, and verify recognition accuracy afterward (do not change it solely in response to a frame-freshness timeout warning).idleTimeoutMs?: number— Idle duration in milliseconds before automatically disconnecting the scrcpy stream (0disables auto-disconnect). Default:30000.videoResetFrameTimeoutMs?: number— Time in milliseconds to wait for a new keyframe after scrcpy triggers an in-band video reset (must be a positive integer). Default:800. Increase this only on devices whose display capture pipeline is slow to restart.
device.getScrcpyStatus()— Returns the current scrcpy state (enabled,connected,lastError, andretryAfter).device.retryScrcpy(): Promise<void>— Bypasses the cooldown timer and immediately retries the scrcpy connection.
Platform methods
launch()
Open a web URL, or launch an Android package or activity.
target: string— Web URL,package/.Activityidentifier (such ascom.android.settings/.Settings), package name, or app name. Iftargetmatches a key inappNameMapping, Midscene resolves it to the mapped package name; otherwise, it launchestargetas provided.
runAdbShell()
Execute a raw adb shell command on the connected device. Pass only the shell command itself, without the adb shell prefix.
command: string— Command string passed verbatim toadb shell(for example,'input tap 100 200', not'adb shell input tap 100 200').opt.timeout?: number— Command execution timeout in milliseconds.
This method invokes the RunAdbShell action under the hood. If exposeRunAdbShellAction is set to false on the AndroidDevice, this method is unavailable.
terminate()
Force-stop a running Android app.
uri: string— Package name, app name registered inappNameMapping, orpackage/.Activitystring (only the package name portion is used).
System navigation
agent.back(): Promise<void>— Trigger the system Back action.agent.home(): Promise<void>— Return to the home screen.agent.recentApps(): Promise<void>— Open the recent apps (multitasking) screen.
Device discovery
Returns the IDs (udid) and states of connected devices. Another exported helper, getConnectedDevicesWithDetails(options?), also queries optional model, brand, resolution, and numeric density fields (returning undefined for any field that cannot be retrieved). Both functions are exported from @midscene/android.
getConnectedDevicesWithDetails()
Lifecycle
Calling agent.destroy() waits for pending operations to complete, calls the underlying Device's destroy(), and finalizes the execution report. Do not share a single Device instance across multiple Agents or reuse a Device after cleanup.
On Android, device.destroy() clears screen caches and adb client state, and closes any active scrcpy connection. It does not disconnect the physical device from the host adb server or close apps on the device.
Troubleshooting
Text input is cleared or disappears after typing
By default, Midscene automatically dismisses the on-screen keyboard after entering text by sending an ESC key event. However, some input fields (especially inside WebViews) listen for the ESC key and trigger side effects such as:
- Clearing the text that was just entered
- Closing the modal or popup containing the input field
- Navigating away from the current page
Try the following workarounds in order:
Option 1: Dismiss the keyboard with the Android BACK key
Set keyboardDismissStrategy to 'back-first' so Midscene sends the Android BACK key instead of ESC:
Option 2: Disable automatic keyboard dismissal
If the input field also reacts to the BACK key, disable automatic keyboard dismissal and let subsequent AI actions manage the keyboard state:
When automatic dismissal is disabled, the keyboard remains on screen and may cover the lower half of the interface. You can handle this by:
- Calling
aiActto collapse the keyboard explicitly, e.g.,await agent.aiAct('tap the collapse button on the keyboard'). - Installing and switching to ADBKeyBoard, a minimal virtual keyboard that takes up almost no screen space even when active.
English text is altered by the Android input method
If the report shows the expected input string, but the app receives garbled characters, missing letters, or Chinese/pinyin candidates, the active Android input method is likely intercepting and rewriting the input. This commonly occurs when pure ASCII text is sent via the native adb input text path while a Chinese IME or predictive keyboard is active.
Set imeStrategy to 'always-yadb' to route all text input through yadb:
In a YAML script:
Or via an environment variable:
Note that this differs from text being cleared after typing: if the text appears properly in the field and then disappears when the keyboard closes, check keyboardDismissStrategy or autoDismissKeyboard above instead.
Screenshots of secure pages (such as password inputs) are black
Apps such as banking or payment clients often set FLAG_SECURE on password entry screens to prevent screen capture, causing screencap to return a black image for the protected region.
The yadb utility creates a virtual display with the secure flag via SurfaceControl.createDisplay, which allows capturing FLAG_SECURE screens in certain environments (actual support depends on the Android version, ROM, root/Magisk hook setup, and device configuration; always test on your target device).
Set screenshotStrategy to 'always-yadb' to force yadb for all screenshots:
In a YAML script:
Or via an environment variable:
By default, 'auto' tries adb.takeScreenshot and shell screencap (or scrcpy first when scrcpyConfig.enabled is true), falling back to yadb only if those commands fail to execute. Because 'auto' does not inspect image contents, it will not automatically switch to yadb when screencap succeeds with a black image—you must explicitly set 'always-yadb' to bypass the default capture chain. Note that yadb only supports the default display (displayId=0); combining 'always-yadb' with a non-zero displayId throws an error.
How do I configure the scrcpy video bitrate?
When using the Android CLI, pass --scrcpy-video-bit-rate <bits-per-second> (or --scrcpyVideoBitRate). Specifying this flag automatically enables scrcpy, so --use-scrcpy can be omitted:
Because each CLI invocation runs in a separate process, pass this flag to every command that needs a custom bitrate. In the JavaScript SDK or YAML scripts, set scrcpyConfig.videoBitRate once in the device configuration. Adjusting the bitrate trades encoding bandwidth against image detail—tune it only when independent transport measurements show a need, and verify recognition accuracy afterward. For defaults and related options, see scrcpy configuration above.
How do I use a custom adb path or remote adb server?
You can configure these via environment variables:
Or pass them in the AndroidDevice constructor (or agentFromAdbDevice):

