ByteScope

screencap

會改變裝置

擷取螢幕畫面,可以寫進手機裡的檔案,也可以把 PNG 送到標準輸出。

指令
adb shell screencap
指令
畫面與擷取
風險
會改變裝置

語法

screencap [-p] [<device path>]

旗標與參數

token意義
-p編成 PNG。既沒有它、路徑也不是 .png 結尾時,輸出是原始的 framebuffer 內容。
<path>寫進裝置上的檔案而不是標準輸出。

範例

  • screencap -p /sdcard/shot.png

    先寫到手機上,再把檔案拉回來。永遠安全。

  • screencap -p

    把 PNG 位元組送到標準輸出。從桌面要用 `adb exec-out`,絕對不要用 `adb shell`。

輸出怎麼讀

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` 一律放行。所以一條已授權的線就夠了,除了 USB 偵錯本身,開發者選項裡沒有東西在管它。你明顯沒有的那個權限是 `android.permission.CAPTURE_BLACKOUT_CONTENT`,也就是能讓安全圖層進入擷取結果的那一個,而 `screencap` 從來不去要它——這才是受保護的影片會變成一塊黑色長方形的機制,不是編碼器壞了。再來是存取問題裡完全不屬於 Android 的那一半:寫進某個路徑就是以 `shell` 使用者做一次普通的 `open`,帶著 `O_WRONLY | O_CREAT | O_TRUNC` 與 0664 模式,所以共用儲存空間可以、app 的私有目錄不行,而你拿回來的拒絕訊息是一句樸素的 C `errno` 字串,不是平台幫它包裝過的東西。

失敗訊息對照

訊息意義
Error opening file: /data/data/com.example/shot.png (Permission denied)`open` 失敗了,括號裡的文字是 C 函式庫的 `strerror`。`shell` 使用者不能寫進另一個 app 的資料目錄;寫到共用儲存空間再把檔案拉回來。同一個位置出現 `Read-only file system` 代表那個路徑在唯讀掛載的分割區上,不是截圖失敗。
Ambiguous file type格式被講了兩次,而且兩次講的不一樣——`-p` 說是 PNG,`.jpg` 或 `.jpeg` 的檔名說是 JPEG。`screencap` 不會替你挑一個,它直接拒絕、以 1 結束,什麼都不寫。副檔名已經帶了格式時,就把 `-p` 拿掉。
Failed to take screenshot. Status: -1擷取請求回來時帶著錯誤狀態,所以沒有任何東西被編碼。SurfaceFlinger 的拒絕會在客戶端這一側浮現成這一行——真正的理由記在那邊而不是印在這裡,所以要去看同一時刻前後的 `logcat`。
Failed to get ID for any displays.SurfaceFlinger 回報沒有實體顯示器可以擷取。開機還沒完成時,以及真的沒有顯示器的 headless 版本上,這是預期中的。它不是權限問題,所以用更高的權限重跑不會有任何改變。
Error writing to stdout. (Bad file descriptor)沒給檔名時,`screencap` 會複製自己的標準輸出來寫入,而那次複製失敗了。實務上這代表串流在它底下被關掉了——管線的讀取端提早離開是最常見的原因。沒有產出任何可用的東西;讀取端已經拿到的是一張被截斷的圖。
[Warning] Multiple displays were found, but no display id was specified!這不是失敗,而它值得列出來的原因正是如此:擷取成功了,但在摺疊機或影像輸出接了東西的手機上,它挑中的那個顯示器不保證下次還是同一個。後面那兩行會告訴你要給 `-d`,以及去哪裡找合法的 id。在這種裝置上沒帶 `-d` 的無人看管擷取,請當成不可靠而不是錯誤。

會改變什麼,以及怎麼回頭

不帶路徑時,它把 PNG 位元組寫到標準輸出,裝置上什麼都不會改變。帶了裝置路徑,它會建立那個檔案,檔案已經存在就直接覆寫並截斷,所以路徑打錯可能蓋掉一個真的檔案,舊內容就沒了——Android 沒有垃圾桶。檔案也會一直占著儲存空間,回頭的路是 `rm <路徑>`;先把它拉到電腦上,截圖才算真的留下來。

版本與廠商注意事項

  • 要串流那串位元組需要 `adb exec-out screencap -p`,不是 `adb shell`。單純的 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,這是從 shell 驅動 SystemUI demo mode 等平台接收器的方法。
wm size回報顯示器的解析度,或覆寫它,讓手機假裝自己是另一塊螢幕。

其他畫面與擷取指令

指令做什麼
wm density回報顯示密度,或覆寫它——真正會讓版面斷點移動的是這一個。

這些行為是在撰寫 Web ADB 工具背後的解析程式時,對照 AOSP、toybox 與核心原始碼查證的。真的會因 Android 版本或廠牌而異的地方,這一頁會直接說明,而不是給你一個在半數手機上都是錯的數字。

常見問題

adb shell 指令要怎麼執行?

在手機上打開開發者選項與 USB 除錯、接上線,然後從裝了 platform-tools 的機器執行 adb shell <指令>——或者只執行 adb shell 取得互動提示字元,再直接打指令、不用前綴。如果指令會輸出二進位資料(例如截圖),請改用 adb exec-out 而不是 adb shell:單純的 shell 服務會配一個終端機,而它會改寫換行位元組並毀掉資料。

adb shell 指令需要 root 嗎?

這份參考裡的都不用。它們以 shell 使用者身分執行,可以針對自己的 Android 使用者管理套件、讀取大多數 dumpsys 服務、寫入設定提供者、注入輸入,以及讀取共用儲存空間。需要 root 的是這份參考刻意避開的事:讀取其他 app 的私有資料(除了對可除錯版本使用 run-as)、寫入系統分割區,或直接讀 /data/anr。

為什麼在我的手機上輸出看起來不一樣?

不是 Android 版本就是廠商。有些旗標與欄位是從特定版本才有的,那些都在各指令頁註明了。其餘是廠商改動,而受影響最深的是 dumpsys:一份 dump 是除錯輸出,它的文字就是那個服務的作者最後印了什麼,而廠商既會改它也會把改動往回移植。請用名稱而不是位置來讀欄位,並把沒見過的格式當成差異而不是失敗。

可以用瀏覽器而不是終端機來跑嗎?

很多指令可以——本站的 Web ADB 工具從 Chromium 系桌面瀏覽器透過 WebUSB 和手機對話,不必安裝 SDK,也不會上傳任何東西。每一頁在有對應工具時都會連過去。有兩個限制值得知道:同一時間只有一個程式能持有 ADB 介面,所以桌面的 adb server 要先停掉;而 adb forward 與 adb reverse 在瀏覽器裡完全無法運作。

比較想按而不是打字?Web ADB Toolkit 會從瀏覽器透過 USB 執行這類指令。或者瀏覽全部指令。