screencap
端末を変えるスクリーンショットを撮ります。端末上のファイルに書くか、PNG として標準出力に流します。
- コマンド
- adb shell screencap
- コマンド
- 画面と取り込み
- リスク
- 端末を変える
構文
screencap [-p] [<device path>]フラグと引数
| トークン | 意味 |
|---|---|
| -p | PNG にエンコードします。これがなく、パスも .png で終わらない場合、出力は生のフレームバッファです。 |
| <path> | 標準出力ではなく端末上のファイルに書きます。 |
使用例
screencap -p /sdcard/shot.png端末に書いてからファイルを取り出します。常に安全です。
screencap -p標準出力に PNG のバイト列。デスクトップからは `adb shell` ではなく必ず `adb exec-out` を通します。
出力の読み方
PNG のバイナリ、あるいはパスを指定した場合は何も出ません。成功メッセージはなく、証拠はファイルかバイト列そのものです。
出力フィールド
| フィールド | 意味 |
|---|---|
| (PNG bytes) | パス指定がないときの、標準出力に流れる画像。 |
| (no output) | 指定したファイルに画像が書かれました。 |
必要な権限
root も `adb root` も不要です。`screencap` は `cmd` の包みではなくネイティブのバイナリで、権限の判断は SurfaceFlinger の内側で起きます。`validateScreenshotPermissions` は呼び出し側が `AID_GRAPHICS`、`AID_SYSTEM`、あるいは `android.permission.READ_FRAME_BUFFER` を持っていれば通し、`shell` ユーザーの正体である `com.android.shell` は自身のマニフェストでそれを与えられています。display id を指定する経路はもっと大ざっぱで、`AID_ROOT`、`AID_GRAPHICS`、`AID_SYSTEM`、`AID_SHELL` を直接通します。だから認可済みのケーブル 1 本で足り、USB デバッグ以外に開発者向けオプションの関門もありません。目立って「持っていない」権限は `android.permission.CAPTURE_BLACKOUT_CONTENT`、secure なレイヤを撮影に含めることを許すもので、`screencap` はそれを要求しません。保護された映像が黒い矩形として届くのはエンコーダの不具合ではなくこの仕組みです。そしてアクセスの話には、まったく Android でない半分もあります。パスへの書き込みは `shell` ユーザーとしての `O_WRONLY | O_CREAT | O_TRUNC`、モード 0664 の普通の `open` なので、共有ストレージは通り、アプリのプライベートディレクトリは通らず、返ってくる拒否はプラットフォームが化粧した何かではなく素の C の `errno` の文字列です。
エラーメッセージの読み解き
| メッセージ | 意味 |
|---|---|
| Error opening file: /data/data/com.example/shot.png (Permission denied) | `open` が失敗し、括弧の中は C ライブラリの `strerror` です。`shell` ユーザーは他のアプリのデータディレクトリの中に書けません。共有ストレージに書いて、そこからファイルを取り出してください。同じ位置の `Read-only file system` は、スクリーンショットが失敗したのではなく、パスが読み取り専用でマウントされたパーティション上にあるという意味です。 |
| Ambiguous file type | 形式が二重に指定され、しかも食い違っています。`-p` は PNG だと言い、`.jpg` や `.jpeg` のファイル名は JPEG だと言っています。`screencap` はどちらかを選ばずに断り、1 で終了して何も書きません。拡張子が既に形式を持っているなら `-p` を落としてください。 |
| Failed to take screenshot. Status: -1 | 撮影の要求がエラーの status で返ったので、何もエンコードされていません。SurfaceFlinger 側の拒否がクライアント側に浮かび上がる場所がここで、理由自体は向こうに記録されていてここには印字されません。同じ時刻あたりの `logcat` が探すべき場所です。 |
| Failed to get ID for any displays. | SurfaceFlinger が、撮影できる物理ディスプレイはないと報告しました。起動が終わる前と、本当に 1 つもない headless のビルドでは想定内です。権限の問題ではないので、より強い権限で実行し直しても何も変わりません。 |
| Error writing to stdout. (Bad file descriptor) | ファイル名がないとき `screencap` は書き込み先として自分の標準出力を複製しますが、その複製が失敗しました。実際には、下でストリームが閉じられたという意味で、読み手が先に終了したパイプラインが日常的な原因です。使えるものは何も生成されておらず、読み手が既に持っているものは切り詰められた画像です。 |
| [Warning] Multiple displays were found, but no display id was specified! | 失敗ではありません。だからこそ載せる価値があります。撮影は成功しますが、折りたたみ端末や映像出力に何かを挿した端末では、選ばれたディスプレイが次回も同じである保証がありません。後に続く 2 行が `-d` を渡すことと有効な id の在り処を教えてくれます。そうした端末で `-d` なしの無人撮影をするなら、間違いではなく当てにならないものとして扱ってください。 |
何が変わるか、そして戻る道
パスなしなら PNG のバイト列を標準出力に書くだけで、端末には何も変更がありません。端末上のパスを指定すると、そのファイルを作成し、すでにあれば上書きして切り詰めるので、パスを間違えると本物のファイルを置き換えてしまい、元の中身は戻りません。Android にはごみ箱がありません。ファイルはストレージを使い続けるため、戻す手段は `rm <パス>` です。スクリーンショットを残したいなら、先にパソコンへ pull しておきましょう。
バージョンとメーカーに関する注意
- バイト列を流すには `adb shell` ではなく `adb exec-out screencap -p` が必要です。素の shell サービスはコマンドに端末を与え、端末はすべての 0x0A を 0x0D 0x0A に変えます。これは何かがデコードしようとするまで見えない形で PNG を壊します。
- DRM で保護された内容は黒くなります。コンポジタがそのバッファを渡すことを拒むので、撮影側の不具合ではなくプラットフォームの性質です。
- 自分の通知アイコンが写らないスクリーンショットが欲しければ、先に `am broadcast` で SystemUI を demo mode にします。
これには UI があります
Android ADB Toolbox はこのコマンドを USB 経由で実行し、返ってきたものを解析します。Chromium 系ブラウザだけで、SDK も不要、どこにもアップロードしません。
関連コマンド
screencap と同じ作業で出てくるコマンド。
| コマンド | 何をするか |
|---|---|
| screenrecord | 端末上の MP4 ファイルに画面を録画します。 |
| am broadcast | ブロードキャスト intent を送ります。SystemUI の demo mode などプラットフォームのレシーバをシェルから動かす方法です。 |
| wm size | ディスプレイの解像度を報告します。上書きすれば、端末は別の画面のふりをします。 |
他の画面と取り込みコマンド
| コマンド | 何をするか |
|---|---|
| wm density | ディスプレイの密度を報告、または上書きします。レイアウトのブレークポイントを実際に動かすのはこちらです。 |
Web ADB ツール群のパーサを書きながら、AOSP・toybox・カーネルのソースに突き合わせて確認しています。バージョンやメーカーで本当に挙動が変わるところは、世界の半分の端末で間違いになる数値を出す代わりに、そう書いてあります。