Skip to content

Android: read the clipboard through a component that satisfies the Android 10 focus/IME gate #2685

Description

@thymikee

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 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.
  • Writes are unchanged. Android does not gate setPrimaryClip the 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.
  • 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.

  1. 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.
  2. 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.
  3. 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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions