ByteScope

screencap

端末を変える

スクリーンショットを撮ります。端末上のファイルに書くか、PNG として標準出力に流します。

コマンド
adb shell screencap
コマンド
画面と取り込み
リスク
端末を変える

構文

screencap [-p] [<device path>]

フラグと引数

トークン意味
-pPNG にエンコードします。これがなく、パスも .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 も不要、どこにもアップロードしません。

Android ADB Toolbox を開く

関連コマンド

screencap と同じ作業で出てくるコマンド。

コマンド何をするか
screenrecord端末上の MP4 ファイルに画面を録画します。
am broadcastブロードキャスト intent を送ります。SystemUI の demo mode などプラットフォームのレシーバをシェルから動かす方法です。
wm sizeディスプレイの解像度を報告します。上書きすれば、端末は別の画面のふりをします。

他の画面と取り込みコマンド

コマンド何をするか
wm densityディスプレイの密度を報告、または上書きします。レイアウトのブレークポイントを実際に動かすのはこちらです。

Web ADB ツール群のパーサを書きながら、AOSP・toybox・カーネルのソースに突き合わせて確認しています。バージョンやメーカーで本当に挙動が変わるところは、世界の半分の端末で間違いになる数値を出す代わりに、そう書いてあります。

よくある質問

adb shell のコマンドはどう実行しますか?

端末で開発者向けオプションと USB デバッグを有効にし、ケーブルを繋いだうえで、platform-tools を入れた機械から adb shell <コマンド> を実行します。adb shell だけを実行すれば対話プロンプトが出て、接頭辞なしで打てます。スクリーンショットのようにバイナリを出力するコマンドでは adb shell ではなく adb exec-out を使ってください。素のシェルサービスは端末を割り当て、それが改行のバイトを書き換えてデータを壊します。

adb shell のコマンドに root は必要ですか?

このリファレンスにあるものには不要です。すべて shell ユーザーとして動き、自分の Android ユーザー向けのパッケージ管理、ほとんどの dumpsys サービスの読み取り、設定プロバイダへの書き込み、入力の注入、共有ストレージの読み取りができます。root が必要なのはこのリファレンスがあえて避けていること、つまり他のアプリのプライベートデータの読み取り(debuggable なビルドに対する run-as を除く)、システムパーティションへの書き込み、/data/anr の直接読み取りです。

自分の端末では出力が違って見えるのはなぜ?

Android のバージョンか、メーカーのどちらかです。一部のフラグやフィールドは特定のリリースから入ったもので、それはコマンドごとに注記しています。残りはメーカーによる改変で、いちばん影響が大きいのは dumpsys です。ダンプはデバッグ出力で、そのテキストはサービスの作者が最後に印字した内容そのもの、しかもメーカーはそれを変えて自分の変更をバックポートします。フィールドは位置ではなく名前で読み、見慣れない形は失敗ではなく差分として扱ってください。

端末ではなくブラウザから実行できますか?

多くのコマンドは可能です。このサイトの Web ADB ツール群は、Chromium 系のデスクトップブラウザから WebUSB で端末と話し、SDK のインストールも不要、どこにもアップロードしません。各ページは、該当するツールがあればそこへリンクしています。知っておく価値のある制限が 2 つあります。ADB インターフェイスを保持できるプログラムは同時に 1 つだけなので、デスクトップの adb server は先に止める必要があります。そして adb forward と adb reverse はブラウザでは一切動きません。

打つより押したい場合は、Web ADB Toolkit がこの種のコマンドをブラウザから USB 経由で実行します。あるいは全コマンドを見る。