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
Make an Android clipboard read able to return real clip contents on a modern Android build.
#2674 diagnosed the empty read as Android 10's (API 29) clipboard-access restriction. That restriction is real, but on every build the CLI can currently reach it is not the binding constraint: no Android build through 16 implements a shell command for the clipboard service, so adb shell cmd clipboard … never reaches ClipboardManager at all — it is answered by Binder.handleShellCommand's framework default (No shell command implementation. on stderr, exit status 0). #2676 makes that answer refuse with UNSUPPORTED_OPERATION instead of reporting an empty clipboard. It does not make a read work, and it cannot: adb has no process that satisfies the focus/IME gate.
This issue is that remaining half: read the clipboard from a component that satisfies the gate, so clipboard read can verify a copy flow on Android 10+ devices.
Required behavior
On a build with no clipboard shell command, clipboard read returns the device's actual primary-clip text when the read route is available on that device.
A read that the route could not perform fails with a typed reason. It never resolves an empty string as an answer: "denied", "no route installed", and "the clipboard is genuinely empty" must be distinguishable by details.reason, never by prose.
capabilities advertises clipboard only when the route that will answer the read is actually present on that device, not when some hypothetical route could exist.
The route is installed and driven through the existing helper-APK lifecycle, with the same first-install-confirmation behavior documented for the snapshot helper and test IME.
Candidate routes
Evaluate in this order; the issue is decided by whichever one proves out on a device.
The bundled test IME. Under --test-ime it is the active input method service and is therefore permitted to call getPrimaryClip(). Needs a read command in its protocol plus lifecycle ownership.
A helper activity brought to the foreground. Satisfies the focus half of the gate, but takes over the screen and changes what the next interaction sees, so it has to state that in its result.
The snapshot helper. Lowest install cost — it is already installed for Android snapshots — but it has neither focus nor IME status today, so it only works if it can be made to hold focus without disturbing the screen.
Observable completion conditions
On an Android 16 (API 36) device, an in-app copy (long-press → Copy in any stock app) is then read back by clipboard read, with the marker text in the JSON result.
pnpm check:affected --run passes, with the new behavior covered at the same seams as the existing clipboard legs: the Android leg tests, the capability-probe tests, and the cross-language golden table if a new reason crosses the runner boundary.
Versioned help (help clipboard), website/docs/docs/commands.md, and website/docs/docs/known-limitations.md state which devices answer a read and what each refusal reason means.
Boundary
// clipboard read --json, route present, copy performed in an app
{ "success": true, "data": { "platform": "android", "action": "read", "text": "SECRET-MARKER-42" } }
// clipboard read --json, route not installed on this device
{
"success": false,
"error": {
"code": "UNSUPPORTED_OPERATION",
"details": { "reason": "android_clipboard_read_route_unavailable" },
"hint": "…"// names the install or activation step, never another adb attempt
}
}
An empty clipboard answers text: "" with a route-reported fact distinguishing it from a denial; the exact fact shape is this issue's first decision.
Dependencies
Depends on fix(android): classify the clipboard shell response, not its exit status #2676 (fix/android-clipboard-shell-response) for the response classifier, the typed android_clipboard_shell_command_unavailable refusal, and the AndroidToolHost.probeClipboardShellSupport fact. That probe's verdict becomes a route identity (shell-command / ime / helper / none) rather than a single supported/unsupported bit; whoever takes this owns that widening.
Out of scope here: classifying pre-shell_v2 merged-stream output, where the missing-shell sentence arrives on stdout and is indistinguishable from clipboard payload.
Purpose
Make an Android clipboard read able to return real clip contents on a modern Android build.
#2674 diagnosed the empty read as Android 10's (API 29) clipboard-access restriction. That restriction is real, but on every build the CLI can currently reach it is not the binding constraint: no Android build through 16 implements a shell command for the clipboard service, so
adb shell cmd clipboard …never reachesClipboardManagerat all — it is answered byBinder.handleShellCommand's framework default (No shell command implementation.on stderr, exit status 0). #2676 makes that answer refuse withUNSUPPORTED_OPERATIONinstead of reporting an empty clipboard. It does not make a read work, and it cannot: adb has no process that satisfies the focus/IME gate.This issue is that remaining half: read the clipboard from a component that satisfies the gate, so
clipboard readcan verify a copy flow on Android 10+ devices.Required behavior
clipboard readreturns the device's actual primary-clip text when the read route is available on that device.details.reason, never by prose.capabilitiesadvertisesclipboardonly when the route that will answer the read is actually present on that device, not when some hypothetical route could exist.setPrimaryClipthe way it gates reads, and fix(android): classify the clipboard shell response, not its exit status #2676's refusal stays the answer on builds where the write cannot be issued at all.Candidate routes
Evaluate in this order; the issue is decided by whichever one proves out on a device.
--test-imeit is the active input method service and is therefore permitted to callgetPrimaryClip(). Needs a read command in its protocol plus lifecycle ownership.Observable completion conditions
clipboard read, with the marker text in the JSON result.pnpm check:affected --runpasses, with the new behavior covered at the same seams as the existing clipboard legs: the Android leg tests, the capability-probe tests, and the cross-language golden table if a new reason crosses the runner boundary.help clipboard),website/docs/docs/commands.md, andwebsite/docs/docs/known-limitations.mdstate which devices answer a read and what each refusal reason means.Boundary
An empty clipboard answers
text: ""with a route-reported fact distinguishing it from a denial; the exact fact shape is this issue's first decision.Dependencies
fix/android-clipboard-shell-response) for the response classifier, the typedandroid_clipboard_shell_command_unavailablerefusal, and theAndroidToolHost.probeClipboardShellSupportfact. That probe's verdict becomes a route identity (shell-command/ime/helper/ none) rather than a single supported/unsupported bit; whoever takes this owns that widening.clipboard readreturns empty string (success:true) on Android 10+ — read is denied, not surfaced #2674, whose reported symptom fix(android): classify the clipboard shell response, not its exit status #2676 already removes.shell_v2merged-stream output, where the missing-shell sentence arrives onstdoutand is indistinguishable from clipboard payload.