Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Crash with Command failed (host:features): more than one device/emulator #64

Open
anmol-tray opened this issue Sep 15, 2023 · 5 comments

Comments

@anmol-tray
Copy link

I am using DADB but it fails with Command failed (host:features): more than one device/emulator

supportedFeatures = open("host:features").use {

Exception in thread "main" java.io.IOException: Command failed (host:features): more than one device/emulator
	at dadb.adbserver.AdbServer.send$dadb(AdbServer.kt:99)
	at dadb.adbserver.AdbServerDadb.open(AdbServer.kt:138)
	at dadb.adbserver.AdbServerDadb.<init>(AdbServer.kt:128)
	at dadb.adbserver.AdbServer.createDadb(AdbServer.kt:52)
	at dadb.adbserver.AdbServer.listDadbs(AdbServer.kt:81)
	at dadb.adbserver.AdbServer.listDadbs$default(AdbServer.kt:60)
	at dadb.Dadb$Companion.list(Dadb.kt:251)
	at dadb.Dadb$Companion.list$default(Dadb.kt:250)
@sleekweasel
Copy link

With a trivial test-rig:

fun main() {
    for (dadb in Dadb.list()) {
        println("$dadb ${dadb.shell("date")}")
    }
}

This patch seems to allow me to access remote TCP devices connected with 'adb connect':


diff --git a/dadb/src/main/kotlin/dadb/adbserver/AdbServer.kt b/dadb/src/main/kotlin/dadb/adbserver/AdbServer.kt
index a481f13..89620d4 100644
--- a/dadb/src/main/kotlin/dadb/adbserver/AdbServer.kt
+++ b/dadb/src/main/kotlin/dadb/adbserver/AdbServer.kt
@@ -53,6 +53,11 @@ object AdbServer {
         val name = deviceQuery
             .removePrefix("host:") // Use the device query without the host: prefix
             .removePrefix("transport:") // If it's a serial-number, just show that
+        if (name.contains(":")) {
+            // Looks like a remote emulator from 'adb connect host:port'
+            val parts = name.split(":")
+            return Dadb.create(parts[0], parts[1].toInt())
+        }
         return AdbServerDadb(adbServerHost, adbServerPort, deviceQuery, name, connectTimeout, socketTimeout)
     }

... I'll try building it into maestro tomorrow. If it fixes this for me, I'll submit a patch.

@sleekweasel
Copy link

Adding the following in the same place makes emulators work, but I still get the 'multiple devices attached' if I also plug in my phone...

        if (name.contains("emulator-")) {
            val parts = name.split("-")
            return Dadb.create(adbServerHost, parts[1].toInt())
        }

@sleekweasel
Copy link

Maybe it's not 'connecting' to the device once adb has selected it, so the host:features is dropped because it's still waiting for a connect?

@sleekweasel
Copy link

sleekweasel commented Dec 3, 2024

I dug into adb's source code and found what looks like the proper fix, replacing all the foregoing:

diff --git a/dadb/src/main/kotlin/dadb/adbserver/AdbServer.kt b/dadb/src/main/kotlin/dadb/adbserver/AdbServer.kt
index a481f13..0d5d301 100644
--- a/dadb/src/main/kotlin/dadb/adbserver/AdbServer.kt
+++ b/dadb/src/main/kotlin/dadb/adbserver/AdbServer.kt
@@ -131,7 +131,7 @@ private class AdbServerDadb constructor(
     private val supportedFeatures: Set<String>
 
     init {
-        supportedFeatures = open("host:features").use {
+        supportedFeatures = open("host-serial:$name:features").use {
             val features = AdbServer.readString(DataInputStream(it.source.inputStream()))
             features.split(",").toSet()
         }

It's mentioned in https://android.googlesource.com/platform/system/adb/+/d3b1f06b5694c84a32ab0349a2aa1f37e3806d95/SERVICES.TXT:

host-serial::
  | This is a special form of query, where the 'host-serial::'
  | prefix can be used to indicate that the client is asking the ADB server
  | for information related to a specific device. can be in one
  | of the format described below.

My test rig now outputs this:

5xxxxxxe Shell response (0):
Tue Dec  3 11:51:37 GMT 2024

automation1.xlan:5535 Shell response (0):
Tue Dec  3 11:51:40 GMT 2024

emulator-5554 Shell response (0):
Tue Dec  3 11:51:37 GMT 2024

emulator-5556 Shell response (0):
Tue Dec  3 11:51:19 GMT 2024

@sleekweasel
Copy link

sleekweasel commented Dec 3, 2024

While the 'host-serial' change works, there's also a 'host-transport-id' in adb's source which might be better.

The advantage and disadvantage of serial numbers vs transport_ids is that transport_ids are new for each plug-in, whereas serial numbers allows the device to disconnect and reconnect. We found serial number handling useful with flaky hubs where the devices would connect and reconnect randomly but quickly enough that tests weren't affected much, but transport_ids are probably better for stable situations.

I've tried making it use transport ids - having deviceQuery 'host:transport-id:5' or whatever - and I'm a bit confused about whether the socket is ever actually irrevocably proxying to the phone, as mentioned in SERVICES.TXT

  • AdbServerDadb's init block is calling open('host:features').
  • This sends 'host:transport-id:5' which is completing successfully, and allegedly tells the server to send the rest of the socket's data to the phone (according to SERVICES.TXT: After the OKAY response, every client request will be sent directly to the adbd daemon running on the device.)
  • But then it sends 'host:features' which looks like it's still being interpreted by the adb server, where the 'host:' prefix expects only one connected device (hence the 'too many device/emulators' message).
  • ...and indeed, if I instead send 'host:devices' I get what looks very much like what I'd expect to get from the server.

I find there's a host-transport-id:x prefix coded in sockets.cpp that insists on ending in : so... I guess that could replace 'host:features', but it would need fiddling.

Anyhow. I have a version of this working with transport_ids, and the serial version. I don't know which one you're more interested in. The transport_ids one is more code.

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

No branches or pull requests

2 participants