ByteScope

screencap

changes the device

Takes a screenshot, either to a file on the phone or to standard output as PNG.

Command
adb shell screencap
Commands
Display and capture
Risk
changes the device

Syntax

screencap [-p] [<device path>]

Flags and arguments

TokenMeaning
-pEncode as PNG. Without it and without a path ending in .png the output is a raw framebuffer dump.
<path>Write to a file on the device instead of to standard output.

Examples

  • screencap -p /sdcard/shot.png

    Write to the phone, then pull the file. Always safe.

  • screencap -p

    PNG bytes on standard output. From a desktop, run it through `adb exec-out`, never `adb shell`.

Reading the output

Binary PNG data, or nothing at all when you gave it a path. There is no success message: the evidence is the file, or the bytes.

Output fields

FieldMeaning
(PNG bytes)The image, on standard output, when no path was given.
(no output)The image went to the file you named.

What access it needs

No root and no `adb root`. `screencap` is a native binary rather than a `cmd` wrapper, and the permission decision happens inside SurfaceFlinger: `validateScreenshotPermissions` lets a caller through if it is `AID_GRAPHICS`, `AID_SYSTEM`, or holds `android.permission.READ_FRAME_BUFFER` — which `com.android.shell`, the package the `shell` user runs as, is granted in its own manifest. The capture-by-display-id path is blunter still and admits `AID_ROOT`, `AID_GRAPHICS`, `AID_SYSTEM` and `AID_SHELL` directly. So an authorised cable is enough, and nothing in developer options gates it beyond USB debugging itself. The permission you conspicuously do not hold is `android.permission.CAPTURE_BLACKOUT_CONTENT`, the one that would allow secure layers into a capture, and `screencap` never asks for it — that is the mechanism behind protected video arriving as a black rectangle, not a fault in the encoder. Then there is the half of the access question that is not Android at all: writing to a path is an ordinary `open` with `O_WRONLY | O_CREAT | O_TRUNC` and mode 0664 as the `shell` user, so shared storage works, an app's private directory does not, and the refusal you get back is a plain C `errno` string rather than anything the platform dressed up.

Failure messages, decoded

MessageMeaning
Error opening file: /data/data/com.example/shot.png (Permission denied)The `open` failed and the text in brackets is the C library's `strerror`. The `shell` user cannot write inside another app's data directory; write to shared storage and pull the file from there. `Read-only file system` in the same position means the path is on a mounted-read-only partition, not that the screenshot failed.
Ambiguous file typeThe format was stated twice and the two statements disagree — `-p` says PNG while a `.jpg` or `.jpeg` filename says JPEG. `screencap` refuses rather than picking one, exits 1, and writes nothing. Drop `-p` when the extension already carries the format.
Failed to take screenshot. Status: -1The capture request came back with an error status, so nothing was encoded. This is where a refusal from SurfaceFlinger surfaces on the client side — the reason itself is logged there, not printed here, so `logcat` around the same moment is the place to look.
Failed to get ID for any displays.SurfaceFlinger reported no physical display to capture. Expected before boot has completed, and on headless builds that genuinely have none. It is not a permission problem, so re-running with more privilege changes nothing.
Error writing to stdout. (Bad file descriptor)With no filename `screencap` duplicates its own standard output to write into, and that duplication failed. In practice it means the stream was closed under it — a pipeline whose reader exited early is the everyday cause. Nothing usable was produced; whatever the reader already had is a truncated image.
[Warning] Multiple displays were found, but no display id was specified!Not a failure, and this is why it is worth listing: the capture succeeds, but on a foldable or a phone with something plugged into its video output the display it picked is not guaranteed to be the same one next time. The two lines after it tell you to pass `-d` and where to find valid ids. Treat an unattended capture without `-d` on such a device as unreliable rather than wrong.

What it changes, and the way back

With no path it writes PNG bytes to standard output and changes nothing on the device. With a device path it creates that file, or overwrites and truncates it if it already exists, so a wrong path can replace a real file and the old contents are gone — Android has no trash can. The file also occupies storage until you remove it: `rm <path>` is the way back, and pulling it to the computer first is what makes a screenshot worth keeping.

Version and vendor notes

  • Streaming the bytes needs `adb exec-out screencap -p`, not `adb shell`. The plain shell service gives the command a terminal, and a terminal turns every 0x0A byte into 0x0D 0x0A — which corrupts a PNG in a way that is invisible until something tries to decode it.
  • Content protected by DRM comes out black. The compositor refuses to hand those buffers over, so this is a property of the platform rather than a bug in the capture.
  • For a screenshot without your own notification icons in it, put SystemUI into demo mode first with `am broadcast`.

We have a UI for this

Android ADB Toolbox runs this command over USB and parses what comes back — a Chromium browser, no SDK, nothing uploaded.

Open Android ADB Toolbox

Related commands

Commands that turn up in the same session as screencap.

CommandWhat it does
screenrecordRecords the screen to an MP4 file on the device.
am broadcastSends a broadcast intent, which is how SystemUI demo mode and other platform receivers are driven from a shell.
wm sizeReports the display's resolution, or overrides it so the phone pretends to be a different screen.

More Display and capture commands

CommandWhat it does
wm densityReports the display density, or overrides it — which is what actually moves a layout breakpoint.

Checked against AOSP, toybox and kernel sources while writing the parsers behind our Web ADB tools. Where a behaviour genuinely varies by Android version or manufacturer, this page says so instead of giving a number that would be wrong on half the phones out there.

Frequently asked questions

How do I run an adb shell command?

Switch on developer options and USB debugging on the phone, plug it in, then run adb shell <command> from a machine with platform-tools installed — or run adb shell on its own to get an interactive prompt and type the command without the prefix. If the command produces binary output, such as a screenshot, use adb exec-out instead of adb shell: the plain shell service attaches a terminal, which rewrites newline bytes and corrupts the data.

Do adb shell commands need root?

Not the ones on this reference. They run as the shell user, which can manage packages for its own Android user, read most dumpsys services, write the settings provider, inject input and read shared storage. Root is only needed for things this reference deliberately avoids — reading another app's private data (except through run-as on a debuggable build), writing system partitions, or reading /data/anr directly.

Why does the output look different on my phone?

Either the Android version or the manufacturer. Some flags and fields arrived in a specific release, and those are noted per command. The rest is OEM patching, which hits dumpsys hardest: a dump is debug output whose text is whatever the service author last printed, and manufacturers change it and backport their changes. Read fields by name rather than by position, and treat an unfamiliar shape as a difference rather than as a failure.

Can I run this from a browser instead of a terminal?

For many commands, yes — the Web ADB tools on this site talk to a phone over WebUSB from a Chromium-based desktop browser, with no SDK installed and nothing uploaded. Each page links to the tool that runs its command, when one exists. Two limits are worth knowing: only one program can hold the ADB interface at a time, so a desktop adb server has to be stopped first, and adb forward and adb reverse cannot work in a browser at all.

Would rather click than type? The Web ADB Toolkit runs commands like this from a browser over USB. Or browse every command.