pull_adb_file derived its host file name from the last path segment of a caller-supplied device path and joined it to the output directory without checking that the name is usable on the host. A device path ending in a separator, a dot segment, a control character, a Windows reserved device name, a trailing dot or space, or a ':' alternate-data-stream marker could resolve to an unexpected host path or fail opaquely after the transfer. The single-file path now derives the name with POSIX semantics and refuses any non-portable segment as invalid_request before creating a directory or running adb pull. This matches the package path's pending host-name policy and never renames a device-reported file. Co-authored-by: morluto <[email protected]>
13 KiB
ADB device inspection and acquisition
REA inspects connected Android devices, reads device state, and transfers
files and packages through the caller's adb binary. Emulators, USB
devices, and network devices participate alike: REA neither starts nor
manages them and never installs platform tools. Every operation composes a
fixed adb argument vector — caller values occupy positional arguments only
and are quoted when adb forwards them to the device shell.
The adb client may start its local server on first device query; that is adb
client behavior, recorded in the tool result when observed.
inspect_adb_client probes only the binary and never contacts the server.
Supply tools
Use any Android platform-tools adb (verified with 34.0.5). Select it
through the environment:
# Optional: absolute path to the adb binary; without it, adb resolves from PATH.
export REA_ADB_PATH=/absolute/path/to/adb
REA never installs the Android SDK, an emulator, or a system image.
Readiness is rea inspect-adb-client: it runs adb version and reports the
resolved binary, its version, and where it was selected from. A missing
binary reports capability_unavailable with the REA_ADB_PATH remediation
rather than attempting an installation.
Answer an analyst question
# Device identity
rea inspect-adb-client
rea list-adb-devices
rea inspect-adb-device emulator-5554
rea inspect-adb-display emulator-5554
rea list-adb-features emulator-5554
rea list-adb-services emulator-5554
# Find and acquire an app (the easy-get flow)
rea resolve-adb-packages emulator-5554 whatsapp
rea list-adb-packages emulator-5554 --scope third_party
rea inspect-adb-package emulator-5554 com.whatsapp
rea pull-adb-package emulator-5554 com.whatsapp --output-directory /tmp/acq
rea inspect-android-package /tmp/acq/com.whatsapp/base.apk
# Device state and files
rea list-adb-processes emulator-5554
rea inspect-adb-window emulator-5554
rea read-adb-setting emulator-5554 global adb_enabled
rea read-adb-logcat emulator-5554 --count 200
rea list-adb-directory emulator-5554 /data/data/com.whatsapp
rea pull-adb-file emulator-5554 /data/local/tmp/notes.txt --output-directory /tmp/acq
rea push-adb-file emulator-5554 /tools/frida-server /data/local/tmp/frida-server
rea capture-adb-screen emulator-5554 --output-directory /tmp/shots
rea collect-adb-bugreport emulator-5554 --output-directory /tmp/bugs
# Package lifecycle and dynamic analysis (device-mutating)
rea install-adb-package emulator-5554 /tmp/app.apk --replace
rea start-adb-app emulator-5554 com.example.app
rea read-adb-logcat emulator-5554 --pid 12345
rea start-adb-activity emulator-5554 android.intent.action.VIEW --data-uri https://example.com
rea stop-adb-app emulator-5554 com.example.app
rea uninstall-adb-package emulator-5554 com.example.app
Equivalent MCP methods use the snake_case names (list_adb_devices,
pull_adb_package, resolve_adb_packages, …). Device operations take
serial exactly as adb devices reports it. The pulled APK set feeds the
existing analysis families directly, for example
rea inspect-android-package /tmp/acq/<package>/base.apk.
Operations
| Tool | Kind | What it does |
|---|---|---|
inspect_adb_client |
read | Resolved binary, version, install path; never contacts the server |
list_adb_devices |
read | Serial, state, transport, model metadata, inferred emulator/physical kind with basis |
inspect_adb_device |
read | Fixed getprop whitelist: build, SDK, patch, fingerprint, product, ABIs, debuggable, ro.kernel.qemu |
inspect_adb_display |
read | wm size and wm density as declared |
list_adb_features |
read | pm list features (hardware/software features, hex GL versions) |
list_adb_services |
read | service list binder services and interfaces |
list_adb_processes |
read | ps -A: user, pid, ppid, rss, state, name |
inspect_adb_window |
read | dumpsys window focus declarations (what is on screen) |
read_adb_setting |
read | One settings get value; device-side null is reported as such |
read_adb_logcat |
read | Bounded logcat -d -t dump for one buffer, optional pid filter |
list_adb_packages |
read | pm list packages -f with scope; base APK device paths |
resolve_adb_packages |
read | Case-insensitive package-name substring search with exact-match flag |
inspect_adb_package |
read | dumpsys package projection: versions, install times, installer, flags, permission count |
list_adb_directory |
read | ls -la: kinds, permissions, owner, group, sizes, symlink targets |
pull_adb_package |
read | Complete split APK set from pm path, each file digested |
pull_adb_file |
read | One absolute device path pulled and digested |
capture_adb_screen |
read | One screencap -p frame (exec-out, no device-side file) with dimensions |
collect_adb_bugreport |
read | Device-generated bugreport zip, digested |
push_adb_file |
mutates device | One local file to a device path (e.g. pushing tools into /data/local/tmp); refuses existing targets without explicit overwrite; verifies via the device's own sha256sum |
install_adb_package |
mutates device | Local APK via adb install [-r]; preserves the device's Success/Failure verdict |
uninstall_adb_package |
mutates device | Removes a package and its data for user 0 |
start_adb_app |
mutates device | Resolves the package's launcher activity (cmd package resolve-activity --brief) and starts it (am start -n) |
start_adb_activity |
mutates device | Starts an intent (am start -a with optional URI, component, and up to 16 typed extras); preserves the activity manager's verdict |
stop_adb_app |
mutates device | Force-stops every process of a package, ending a dynamic-analysis run |
Interpret the result
- Inferences carry their basis. Device
kindreportskind_basis(serial prefix or USB transport); APKrolereportsrole_basis: file_name;emulator_observedis the build's ownro.kernel.qemuvalue,nullwhen absent. - Device paths are not host paths.
base_apk_device_pathvalues live on the device filesystem. Only pulls and captures produce host-local paths, each paired with byte size and SHA-256 digest. A pulled file's host name is the device path's last segment, accepted only when every host can store it: apull_adb_filewhose device path ends in a separator,./.., a control or reserved character, a Windows reserved device name, a trailing dot or space, or a:alternate-data-stream marker is refused asinvalid_requestbefore anyadb pullruns, and REA never renames a device-reported file. - Splits are part of the answer.
pm pathenumerates the base APK and every installed split; pulls cover the whole set. System packages whose APKs keep other names report theunknownrole rather than guessing. - Partial results keep facts. Failed individual pulls return successful
artifacts with
coverage: "partial"and per-filefailures. Listings that contain unrecognized lines report them asunparsed_linesand mark partial coverage instead of dropping rows. - Mutations are explicit.
push_adb_file,install_adb_package, anduninstall_adb_packagedeclaremutatesTargetand never run implicitly: pushes refuse existing device files unlessoverwriteis set and verify the transfer with the device's ownsha256sum; installs preserve the device'sFailure [...]reason verbatim. - Producer identity stays attached. Every result embeds the producing
adb client (
binary_path,path_source,version,installed_path).
Resource and lifecycle limits
- Caller values reach adb only as positional arguments in fixed vectors;
no tool accepts an arbitrary shell command line, and UI interaction
(
input,am,monkey) is deliberately not exposed. - Per-invocation budgets: client/devices 10-20 s, shell reads 20-60 s with
64 KiB-8 MiB capture caps (logcat 4 MiB, screen 32 MiB), transfers and
bugreports 600 s. Exceeded budgets report
resource_constraint; stopped commands reportprovider_timeout. - Pull and capture outputs are additive: the destination must not already exist. Re-running a completed pull refuses the existing directory instead of overwriting evidence.
- Cancellation during a package pull preserves caller-selected output. The error includes partial observations identifying completed artifacts and the interrupted local path; remaining APKs were not attempted.
- Failed package acquisition rollback retains its directory identity and retries cleanup before later work and during shutdown. Cleanup refuses a replacement directory. Terminal shutdown stops admission and drains active operations.
- The provider holds no long-lived processes; every invocation is one bounded adb run. The adb server belongs to the caller.
Related capability and follow-up lanes
UI interaction beyond intent starts (tapping, typing, monkey fuzzing, screen
recording) remains a separate lane: candidates such as
mobile-device-mcp install
driver software on the device and pair with the Frida instrumentation lane
(#1359) behind explicit mutation-authority contracts. Network transports
beyond already-connected devices (adb connect) are not exposed yet.
Real verification
npm run verify:adb exercises every operation against a live device. It
requires adb (or REA_ADB_PATH) and any attached emulator or phone;
--serial selects one when several are attached, --pull PACKAGE pulls a
real package and verifies the returned digests against the files on disk,
and --install-apk PATH drives a full install → resolve → uninstall
lifecycle with a lane-owned probe.
Record: verified against adb 34.0.5 on Linux against an Android 14
(API 34) x86_64 emulator — all operations above, a 224-package inventory,
287 services, 92 features, a real two-APK split set pulled with byte-exact
digests, a verified push/pull roundtrip, a 1.3 MB screen capture, and the
complete package lifecycle; see docs/testing.md for the lane summary.