Skip to content

fix(android): control emulators argent can't find over adb - #1397

Draft
blazejkustra wants to merge 3 commits into
mainfrom
fix/android-external-emulator
Draft

blazejkustra wants to merge 3 commits into
mainfrom
fix/android-external-emulator

Conversation

@blazejkustra

@blazejkustra blazejkustra commented Oct 12, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

An Android emulator started outside argent under a different home directory (for example by another tool or user account) could not be screenshotted, tapped or swiped: argent did not find it, and each of those calls failed.

Approach

argent now falls back to controlling such an emulator over adb, the way it already controls physical phones. That path does not need to find the emulator first, so it works.

Tests

  • Live on an API 35 AVD started with HOME=<other dir> emulator -avd <avd> -read-only -no-window: on main, screenshot, gesture-tap and gesture-swipe failed (3 of 3); with this PR all three work (search opened, app drawer opened).
  • button and keyboard go through adb and work on both builds.
  • A plain emulator -read-only -no-window or token-less -grpc 8554 launch already works on main, so it is unaffected.
  • New blueprint unit test for the fallback; the full tool-server suite passes.
  • No docs change: the docs don't describe this limitation.
  • Not verified: Linux CI runners, Android Studio.

Screenshots

Before

android-emu-before.mp4

After

android-emu-after.mp4

…ontroller

An emulator started outside argent under another HOME (a CI runner, a service
account) has no gRPC discovery file where argent looks, so every screenshot,
tap and swipe failed with 'not found among running emulators'. Retry such
emulators with the screen-sharing (android_device) controller, which only
needs adb.
@coderabbitai

coderabbitai Bot commented Oct 12, 2026

Copy link
Copy Markdown
Contributor

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@blazejkustra blazejkustra changed the title fix(android): drive emulators without a gRPC bridge through the adb controller fix(android): control emulators argent can't find over adb Oct 12, 2026

@latekvo latekvo left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Diplomat, Opus 5.5]: Ran the PR on a real Pixel_6_API_34 emulator started under a separate HOME, comparing this PR with main and with a normally started emulator: screenshot, tap, swipe, pinch, two-finger gesture-custom, rotate, paste, keyboard, buttons, screen recording, stop-simulator-server respawn, and two tool-servers on one device. Ran the shipped Linux simulator-server in a container to check the discovery error paths. Also ran prettier, eslint, both tool-server typechecks and the full tool-server vitest suite (all green), checked that the new test fails on main, and mutation-tested the fallback guard. The core claim reproduces: on main, screenshot, tap and swipe fail on that emulator, and with this PR they work.

Comment thread packages/tool-server/src/blueprints/simulator-server.ts Outdated
`[sim ${device.id}] emulator not found through gRPC discovery; ` +
`falling back to the adb screen-sharing controller\n`
);
return spawnSimulatorServerProcess(device.id, "android_device");

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Diplomat, Opus 5.5]: Once an emulator falls back here, two-finger gestures report success but do nothing. Measured on a Pixel_6_API_34 started with HOME=<tmp> emulator -avd Pixel_6_API_34 -read-only -no-window, with Google Maps open:

  • gesture-pinch (centre 0.5,0.35, distance 0.1 -> 0.5) returned pinched:true.
  • gesture-custom with x2/y2 returned 200.
  • Neither changed the zoom in 3 attempts. A single-finger pan on the same server works.

The same two calls on main, against a normally started emulator on the android controller, zoom in and out. The device keeps kind: "emulator", so nothing downstream knows it is now behind the phone controller. Before this change these calls failed with SIMULATOR_SERVER_READY_EXITED; now they report a gesture that never happened.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leaving open: making two-finger gestures work on the phone controller, or rejecting them for a fallen-back emulator (it keeps kind: "emulator"), is a design call for a human, not an unattended change.

device.id,
subcommandForDevice(device)
);
const { proc, apiUrl, streamUrl } = await spawnWithEmulatorFallback(device);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Diplomat, Opus 5.5]: A tap that is the first call to reach a fallen-back emulator is reported as done but does not land. Each trial: Settings open, search bar frame from uiautomator dump [42,591][1038,728] on a 1080x2400 screen, then stop-simulator-server, then gesture-tap at (0.5, 0.2747), then the top activity after 2 s.

Run Tap result Top activity
This PR, fallen-back emulator, tap as the first call tapped:true (~1.1 s) .Settings in 3/3
This PR, same, one screenshot before the tap tapped:true SearchActivity in 3/3
main, normal emulator, tap as the first call tapped:true SearchActivity in 3/3

Every respawn of this service is exposed: first use, after stop-simulator-server, and after the registry's connection-refused recovery. Each one hands back a server whose first touch is silently dropped.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leaving open: the dropped first tap needs a warm-up or readiness fix verified on a real emulator, which I did not do unattended.

`[sim ${device.id}] emulator not found through gRPC discovery; ` +
`falling back to the adb screen-sharing controller\n`
);
return spawnSimulatorServerProcess(device.id, "android_device");

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Diplomat, Opus 5.5]: Two tool-servers driving one fallen-back emulator evict each other. Each android_device start kills the screen-sharing agents already on the device, so the other server's simulator-server exits (status 143) and respawns on its next call, at 1.3-1.6 s per alternating call. The tap after such a respawn is the dropped first tap described on line 598. Two android servers on a normally started emulator coexist, with every frame fresh.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leaving open: two tool-servers evicting each other comes from the android_device controller killing existing agents; fixing that is simulator-server work or a product decision on whether to accept it for this fallback.

`[sim ${device.id}] emulator not found through gRPC discovery; ` +
`falling back to the adb screen-sharing controller\n`
);
return spawnSimulatorServerProcess(device.id, "android_device");

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Diplomat, Opus 5.5]: The phone controller leaves state on the emulator that the emulator controller never touches:

  • Each spawn adds an adb reverse for screen-sharing-agent-* that is never removed. There were 11 after one session of restarts.
  • The 9.5 MB agent stays in /data/local/tmp/.studio.
  • rotate sets accelerometer_rotation=0 and leaves it there. On main's emulator path it stays 1.

For an emulator someone else started, all of this persists after argent is gone.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leaving open: cleaning up the adb reverse, the pushed agent and accelerometer_rotation on an emulator argent did not start needs a decision on what argent should own and undo.

`[sim ${device.id}] emulator not found through gRPC discovery; ` +
`falling back to the adb screen-sharing controller\n`
);
return spawnSimulatorServerProcess(device.id, "android_device");

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Diplomat, Opus 5.5]: Screenshots through the fallback come from a 920x2044 stream instead of the emulator's 1080x2400. A default-scale screenshot is 230x511 here and 270x600 on the gRPC path of the same AVD. So screenshot-diff reports dimension_mismatch when one side was captured on the normal path and the other through this fallback.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leaving open: the 920x2044 stream size comes from the phone controller; matching the gRPC path resolution (or documenting the mismatch) needs a human decision.

Comment thread packages/tool-server/src/blueprints/simulator-server.ts Outdated
…ectory

On Linux, a user with no avd/running directory gets 'Failed to find any
running emulator' instead of 'not found among running emulators', so the
fallback never triggered there. Match both, keep the emulator controller's
error when the adb fallback fails too, and update the comments that said
emulators always use the gRPC bridge.

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants