You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Android: clipboard read returns empty string (success:true) on Android 10+ — read is denied, not surfaced #2674
clipboard read on a modern Android device returns an empty string with success: true, indistinguishable from a genuinely empty clipboard. Android 10+ (API 29) restricts clipboard reads to the foreground app or the active IME, and agent-device's reader is neither — so the read is silently denied rather than surfaced as an error. This makes clipboard read unusable as written on physical devices, and worse, silently wrong: an agent testing "did the app copy this token to the clipboard?" gets "" and concludes nothing was copied.
Issue #92 (which added the command) listed "Supported on iOS simulator and Android emulator/device (document any limits)" as acceptance criteria; this is that undocumented limit.
Steps to reproduce (stock Settings app, no custom app needed)
The write genuinely lands (the OS clipboard service holds the value — adb shell dumpsys clipboard confirms a primary clip exists); only the read comes back blank.
Cause (hypothesis)
Since Android 10 (API 29), ClipboardManager.getPrimaryClip() returns null/empty unless the caller is the current default IME or the app with input focus (see the platform's clipboard access notes). Whatever process agent-device uses to read the clipboard does not satisfy that gate on a physical device, so the framework returns an empty clip and the command reports it verbatim.
Impact
clipboard read cannot verify copy/"Copy link"/OTP-to-clipboard flows on a real device — the primary use cases from Expose clipboard read and write #92.
It fails silently (success: true, empty string), which is worse than an error: callers can't distinguish "denied" from "empty".
Suggested fix
Route the read through a component that satisfies the API 29 gate — e.g. read via the bundled test IME (which is the active IME under --test-ime and is therefore permitted getPrimaryClip), or via a small foreground helper activity. Failing that, at minimum return a typed error/warning (e.g. reason: clipboard_read_denied) instead of an empty string, and document the limit on help clipboard.
Environment
agent-device 0.21.6 (npm latest)
Device: Pixel 8a, Android 16 (API 36), physical device over USB
#2676 fixes the silent answer you reported: on a build like this, clipboard read and clipboard write now fail with UNSUPPORTED_OPERATION plus a hint, and capabilities stops listing clipboard instead of advertising a clipboard no adb call ever touched.
The cause is one step earlier than the Android 10 clipboard gate in your hypothesis: cmd clipboard never reaches ClipboardManager, because the clipboard service implements no shell command for cmd to call. ClipboardService carries no ShellCommand at android13-release, android14-release, android15-release or android16-release, nor on current AOSP main, so every cmd clipboard … call is answered by Binder.handleShellCommand's framework default — No shell command implementation. printed to stderr, and exit status 0. The old code consulted the exit status first, which is why the write reported "Clipboard updated" and the read reported text: "".
Two commands would confirm your device is on that route, @theblackturtle:
adb -s <serial> shell cmd clipboard set text MARKER-42; echo "rc=$?"
adb -s <serial> shell cmd clipboard get text; echo "rc=$?"
If both print No shell command implementation. with rc=0, this is the case #2676 covers. If the device prints something else, please post it — that would be a build which implements the command, where the blank read is the Android 10 focus/IME gate, and that is tracked separately in #2685.
Worth knowing: dumpsys clipboard can't settle this either way. On an Android 16 user build that command writes nothing at all to the shell user — 0 bytes, against ~58 KB for dumpsys window — so a primary clip visible there came from a copy inside an app, which is precisely the copy an adb-side read cannot observe.
Summary
clipboard readon a modern Android device returns an empty string withsuccess: true, indistinguishable from a genuinely empty clipboard. Android 10+ (API 29) restricts clipboard reads to the foreground app or the active IME, andagent-device's reader is neither — so the read is silently denied rather than surfaced as an error. This makesclipboard readunusable as written on physical devices, and worse, silently wrong: an agent testing "did the app copy this token to the clipboard?" gets""and concludes nothing was copied.Issue #92 (which added the command) listed "Supported on iOS simulator and Android emulator/device (document any limits)" as acceptance criteria; this is that undocumented limit.
Steps to reproduce (stock Settings app, no custom app needed)
Expected
text: "SECRET-MARKER-42"; got"".The write genuinely lands (the OS clipboard service holds the value —
adb shell dumpsys clipboardconfirms a primary clip exists); only the read comes back blank.Cause (hypothesis)
Since Android 10 (API 29),
ClipboardManager.getPrimaryClip()returnsnull/empty unless the caller is the current default IME or the app with input focus (see the platform's clipboard access notes). Whatever processagent-deviceuses to read the clipboard does not satisfy that gate on a physical device, so the framework returns an empty clip and the command reports it verbatim.Impact
clipboard readcannot verify copy/"Copy link"/OTP-to-clipboard flows on a real device — the primary use cases from Expose clipboard read and write #92.success: true, empty string), which is worse than an error: callers can't distinguish "denied" from "empty".Suggested fix
Route the read through a component that satisfies the API 29 gate — e.g. read via the bundled test IME (which is the active IME under
--test-imeand is therefore permittedgetPrimaryClip), or via a small foreground helper activity. Failing that, at minimum return a typed error/warning (e.g.reason: clipboard_read_denied) instead of an empty string, and document the limit onhelp clipboard.Environment