Skip to content

Android: Split (google) emulators across different adb servers#4900

Open
d4vidi wants to merge 9 commits intomasterfrom
feat/multi-adb-server
Open

Android: Split (google) emulators across different adb servers#4900
d4vidi wants to merge 9 commits intomasterfrom
feat/multi-adb-server

Conversation

@d4vidi
Copy link
Collaborator

@d4vidi d4vidi commented Jan 6, 2026

Description

  • This pull request addresses the issue described here: #<?>

This pull request aims to greatly increase stability in Android (google) emulator devices in a multi-devices environment. It addresses the following problem (no issue reported so I'm writing it here) -

Problem

With local emulators being shifted back into the main focus, we've recently started noticing on numerous environments (local, CI) that with Detox running over more than 1 emulator concurrently, the ADB server gets "lost". Namely it often cannot handle concurrently executed ADB commands, thus resulting in frequent ADB command failures. This phenomenon has become more noticeable with the recent introduction of the automated device status-bar configuration feature.

Solution

The ADB ↔ emulator framework supports multiple ADB servers and so this PR introduces a shift to an architecture by which each dispatched emulator gets its own ADB server, listening on a dedicated port.

Initializing an ADB server with a port can be done using either the -P <port> argument or the ANDROID_ADB_SERVER_PORT env var.

Starting up an emulator while specifying a custom ADB server port can be done strictly via the ANDROID_ADB_SERVER_PORT env var.

This solution prefers the -P arg when possible - since it's more verbose, but otherwise resorts to the env-var. This takes into account the fact emulator allocation is performed by the primary context - which is fundamentally a single process, and the runtime execution of tests commands is performed by worker - each running in its own process.

Debts

  • Strict port allocation. For now, we work inside the open-range starting from the default ADB server port (5037) + 1 (i.e. 5038). We implicitly assume the 5037+ ports are either free or strictly used by adb servers.
  • Apply this technique to Geny-cloud and attached (real) Android devices?
    Note: Didn't do this because I haven't looked into whether there exists a (clean?) way to connect those devices onto specific servers (i.e. specify the port).
  • Protect using a dedicated "toggle" since this has breaking potential (in some envs)

For features/enhancements:

  • I have added/updated the relevant references in the documentation files.

For API changes:

  • I have made the necessary changes in the types index file.

@d4vidi d4vidi requested a review from noomorph as a code owner January 6, 2026 09:51
@d4vidi d4vidi force-pushed the feat/multi-adb-server branch from 3dae97a to 7ece119 Compare January 8, 2026 11:38
@d4vidi d4vidi force-pushed the feat/multi-adb-server branch from 7ece119 to 626a501 Compare January 8, 2026 11:39
@d4vidi d4vidi force-pushed the feat/multi-adb-server branch from c143a81 to d9af687 Compare January 11, 2026 15:25
@d4vidi d4vidi self-assigned this Jan 12, 2026
const ports = [this._adb.defaultServerPort];

if (useSeparateAdbServers) {
for (let port = this._adb.defaultServerPort + 1; await isPortTaken(port); port++) {
Copy link
Collaborator

@noomorph noomorph Jan 12, 2026

Choose a reason for hiding this comment

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

What's the guarantee that the port is taken by an ADB server actually?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Can't we just enumerate ports from adbPortRegistry?

}

const maxPortDevice = _.maxBy(currentDevices, 'adbServerPort');
return _.get(maxPortDevice, 'adbServerPort', this._adb.defaultServerPort) + 1;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do you check that it is actually free?

Copy link
Collaborator

@noomorph noomorph left a comment

Choose a reason for hiding this comment

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

I feel a bit uneasy about this change. That process.env.* mutation to enable third party reporters – I see where you are coming from, but it is, to say the least, very puzzling situation... The tradeoff is real - one worker = one device at time, but yes, we can allow to accumulate a bit more tech debt. I'm more puzzled that after 4-5 years of using Android emulators outside gmsaas we never caught issues like that... if the flakiness appears specifically near device allocation (status bar tweaks), did you double check that there's no way something else is missing?

Overall, the PR is straightforward enough - you augmented device cookie, now you use also adb server port. The port allocation seems fishy to me in the current implementation - on CI scale, the risk is high enough that something will occupy presumably an ADB'ish port, and are you well equipped to handle this case? 🤔

The port allocation logic seems to be suspicious enough that I'd ask you to stop and look, please. 🙏

@d4vidi
Copy link
Collaborator Author

d4vidi commented Jan 13, 2026

@noomorph thanks for the review! I'd rather we talk in person but in any case here is where I was coming from with this solution:

  • Yes indeed, the port allocation solution is as naive as it gets. Nevertheless, we've always run under the assumption that port 5037 is free and there were no issues with that; As a matter of fact, this port is used not just by detox users, but by any Android / react-native developer out there that has ever run an emulator. So this puts that assumption on the very safe side IMO. That is why I was hesitant to try to introduce a more robust soltuion (but applied of a protection toggle nonetheless).
  • That said, the alternatives I did have in mind are in these 2 directions:
    • Random port
    • Scanning for free ports within a constant range (e.g. 5038-5067 - thus also implicitly limiting the amount of concurrent adb servers)
  • process.env -- well, I'm not overly fond of it myself but I didn't have a better idea. I'm not sure it's that bad.

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