nmeum / android-tools

Unoffical CMake-based build system for android command line utilities
Apache License 2.0
189 stars 56 forks source link

adb fails to find devices over USB with libusb (android-tools 35.0.1) #153

Open omasanori opened 2 months ago

omasanori commented 2 months ago

android-tools 35.0.0 and later uses libusb by default on Linux. (See also: https://developer.android.com/tools/releases/platform-tools)

On Fedora Linux 40 x86_64 and postmarketOS v24.06 aarch64 (based on Alpine Linux v3.20) which packages android-tools 35.0.1, adb fails to detect devices over USB.

A workaround is adding ADB_LIBUSB=0 to the environment. (ex. ADB_LIBUSB=0 adb devices -l)

Downstream report: https://gitlab.alpinelinux.org/alpine/aports/-/issues/16326

Biswa96 commented 2 months ago

I am using adb in ArchLinux without any issue. Please let me know if there is any way I could help to troubleshoot the issue. ADB_TRACE environment variable may help to show any debug output.

Arnavion commented 2 months ago

Looking at ADB_TRACE=all adb server nodaemon, I see:

08-01 00:25:08.657 16626 16626 D adb     : adb_trace.cpp:187 Android Debug Bridge version 1.0.41
08-01 00:25:08.657 16626 16626 D adb     : adb_trace.cpp:187 Version 35.0.1-android-tools
08-01 00:25:08.657 16626 16626 D adb     : adb_trace.cpp:187 Installed as /usr/bin/adb
08-01 00:25:08.657 16626 16626 D adb     : adb_trace.cpp:187 Running on Linux 6.9.10 (aarch64)
08-01 00:25:08.657 16626 16626 D adb     : adb_trace.cpp:187 
08-01 00:25:08.657 16626 16626 D adb     : commandline.cpp:1646 Using server socket: tcp:5037
08-01 00:25:08.658 16626 16626 D adb     : usb_libusb.cpp:1029 initializing libusb...
08-01 00:25:08.660 16626 16626 D adb     : transport_local.cpp:233 transport: local client init
08-01 00:25:08.661 16626 16630 D adb     : transport_local.cpp:191 transport: client_socket_thread() starting
08-01 00:25:08.661 16626 16626 I adb     : auth.cpp:416 adb_auth_init...
08-01 00:25:08.663 16626 16626 I adb     : auth.cpp:152 loaded new key from '/root/.android/adbkey' with fingerprint 7085CFED43D7E255B15677E4FD60E5FC24D9F3535188265308C2330D0D3D9177
08-01 00:25:08.663 16626 16626 I adb     : auth.cpp:391 adb_auth_inotify_init...
08-01 00:25:08.665 16626 16626 D adb     : main.cpp:232 Event loop starting
08-01 00:25:11.665 16626 16626 D adb     : fdevent_epoll.cpp:165 (fdevent 0: fd 4 R) got events 0x1

According to adb/client/usb_libusb.cpp, after initializing libusb... log, it calls libusb_init, then calls libusb_hotplug_register_callback(LIBUSB_HOTPLUG_ENUMERATE, hotplug_callback). (libusb_hotplug_register_callback(LIBUSB_HOTPLUG_ENUMERATE) is expected to call the callback with all existing devices.) hotplug_callback spawns a thread to run the hotplug_thread function. The first thing the hotplug_thread thread does is log libusb hotplug thread started and set its name to libusb hotplug, but this log does not appear above. Furthermore I do not see a thread named libusb hotplug in the list of threads of adb process.

$ pidof adb

16626

$ ls -l /proc/16626/task/

total 0
dr-xr-xr-x    7 root     root             0 Aug  1 00:25 ./
dr-xr-xr-x    9 root     root             0 Aug  1 00:25 ../
dr-xr-xr-x    7 root     root             0 Aug  1 00:25 16626/
dr-xr-xr-x    7 root     root             0 Aug  1 00:25 16627/
dr-xr-xr-x    7 root     root             0 Aug  1 00:25 16628/
dr-xr-xr-x    7 root     root             0 Aug  1 00:25 16629/
dr-xr-xr-x    7 root     root             0 Aug  1 00:25 16630/

$ ps -T

...

16626 root      0:00 adb server nodaemon
16627 root      0:00 adb server nodaemon
16628 root      0:00 {libusb_event} adb server nodaemon
16629 root      0:00 {libusb} adb server nodaemon
16630 root      0:00 {client_socket_t} adb server nodaemon

(libusb_event is started internally by libusb itself.)

So it would seem that the problem is libusb isn't sending any events to the hotplug_callback. atm I can't check in gdb to be sure because the Alpine package of android-tools doesn't have debug symbols; I'll need to recompile with symbols enabled.

Arnavion commented 2 months ago

Forgot to say, according to strace it certainly seems to be enumerating all the USB devices. But again I don't have debug symbols right now to see what exactly libusb is doing.

$ ADB_TRACE=all strace -fe file,read,readv,write,writev -- adb server nodaemon

...

[pid 20695] writev(2, [{iov_base="", iov_len=0}, {iov_base="08-01 00:41:41.531 20695 20695 D"..., iov_len=86}], 208-01 00:41:41.531 20695 20695 D adb     : usb_libusb.cpp:1029 initializing libusb...
) = 86
[pid 20695] write(6, "\1\0\0\0\0\0\0\0", 8) = 8
[pid 20695] openat(AT_FDCWD, "/dev/bus/usb", O_RDONLY|O_LARGEFILE|O_CLOEXEC|O_DIRECTORY) = 8
[pid 20695] statfs("/sys", {f_type=SYSFS_MAGIC, f_bsize=4096, f_blocks=0, f_bfree=0, f_bavail=0, f_files=0, f_ffree=0, f_fsid={val=[0x52a078e6, 0xe4a7715]}, f_namelen=255, f_frsize=4096, f_flags=ST_VALID|ST_NOSUID|ST_NODEV|ST_NOEXEC|ST_RELATIME}) = 0
strace: Process 20697 attached
[pid 20695] openat(AT_FDCWD, "/sys/bus/usb/devices", O_RDONLY|O_LARGEFILE|O_CLOEXEC|O_DIRECTORY) = 10
[pid 20695] openat(AT_FDCWD, "/sys/bus/usb/devices/usb3/busnum", O_RDONLY|O_LARGEFILE|O_CLOEXEC) = 11
[pid 20695] read(11, "3\n", 19)         = 2
[pid 20695] openat(AT_FDCWD, "/sys/bus/usb/devices/usb3/devnum", O_RDONLY|O_LARGEFILE|O_CLOEXEC) = 11
[pid 20695] read(11, "1\n", 19)         = 2
[pid 20695] openat(AT_FDCWD, "/sys/bus/usb/devices/usb3/speed", O_RDONLY|O_LARGEFILE|O_CLOEXEC) = 11
[pid 20695] read(11, "12\n", 19)        = 3
[pid 20695] openat(AT_FDCWD, "/sys/bus/usb/devices/usb3/descriptors", O_RDONLY|O_LARGEFILE|O_CLOEXEC) = 11
[pid 20695] read(11, "\22\1\20\1\t\0\0@k\35\1\0\t\6\3\2\1\1\t\2\31\0\1\1\0\340\0\t\4\0\0\1"..., 256) = 43
[pid 20695] openat(AT_FDCWD, "/sys/bus/usb/devices/usb1/busnum", O_RDONLY|O_LARGEFILE|O_CLOEXEC) = 11
[pid 20695] read(11, "1\n", 19)         = 2
[pid 20695] openat(AT_FDCWD, "/sys/bus/usb/devices/usb1/devnum", O_RDONLY|O_LARGEFILE|O_CLOEXEC) = 11
[pid 20695] read(11, "1\n", 19)         = 2
[pid 20695] openat(AT_FDCWD, "/sys/bus/usb/devices/usb1/speed", O_RDONLY|O_LARGEFILE|O_CLOEXEC) = 11
[pid 20695] read(11, "480\n", 19)       = 4
[pid 20695] openat(AT_FDCWD, "/sys/bus/usb/devices/usb1/descriptors", O_RDONLY|O_LARGEFILE|O_CLOEXEC) = 11
[pid 20695] read(11, "\22\1\0\2\t\0\0@k\35\2\0\t\6\3\2\1\1\t\2\31\0\1\1\0\340\0\t\4\0\0\1"..., 256) = 43
[pid 20695] openat(AT_FDCWD, "/sys/bus/usb/devices/1-1/busnum", O_RDONLY|O_LARGEFILE|O_CLOEXEC) = 11
[pid 20695] read(11, "1\n", 19)         = 2
[pid 20695] openat(AT_FDCWD, "/sys/bus/usb/devices/1-1/devnum", O_RDONLY|O_LARGEFILE|O_CLOEXEC) = 11
[pid 20695] read(11, "5\n", 19)         = 2
[pid 20695] openat(AT_FDCWD, "/sys/bus/usb/devices/1-1/speed", O_RDONLY|O_LARGEFILE|O_CLOEXEC) = 11
[pid 20695] read(11, "480\n", 19)       = 4
[pid 20695] openat(AT_FDCWD, "/sys/bus/usb/devices/1-1/descriptors", O_RDONLY|O_LARGEFILE|O_CLOEXEC) = 11
[pid 20695] read(11, "\22\1\0\2\357\2\1@|,%\1\30\3\1\2\3\1\t\2\350\0\6\1\0\240\372\t\4\0\0\2"..., 256) = 250
[pid 20695] openat(AT_FDCWD, "/sys/bus/usb/devices/usb4/busnum", O_RDONLY|O_LARGEFILE|O_CLOEXEC) = 11
[pid 20695] read(11, "4\n", 19)         = 2
[pid 20695] openat(AT_FDCWD, "/sys/bus/usb/devices/usb4/devnum", O_RDONLY|O_LARGEFILE|O_CLOEXEC) = 11
[pid 20695] read(11, "1\n", 19)         = 2
[pid 20695] openat(AT_FDCWD, "/sys/bus/usb/devices/usb4/speed", O_RDONLY|O_LARGEFILE|O_CLOEXEC) = 11
[pid 20695] read(11, "12\n", 19)        = 3
[pid 20695] openat(AT_FDCWD, "/sys/bus/usb/devices/usb4/descriptors", O_RDONLY|O_LARGEFILE|O_CLOEXEC) = 11
[pid 20695] read(11, "\22\1\20\1\t\0\0@k\35\1\0\t\6\3\2\1\1\t\2\31\0\1\1\0\340\0\t\4\0\0\1"..., 256) = 43
[pid 20695] openat(AT_FDCWD, "/sys/bus/usb/devices/usb2/busnum", O_RDONLY|O_LARGEFILE|O_CLOEXEC) = 11
[pid 20695] read(11, "2\n", 19)         = 2
[pid 20695] openat(AT_FDCWD, "/sys/bus/usb/devices/usb2/devnum", O_RDONLY|O_LARGEFILE|O_CLOEXEC) = 11
[pid 20695] read(11, "1\n", 19)         = 2
[pid 20695] openat(AT_FDCWD, "/sys/bus/usb/devices/usb2/speed", O_RDONLY|O_LARGEFILE|O_CLOEXEC) = 11
[pid 20695] read(11, "480\n", 19)       = 4
[pid 20695] openat(AT_FDCWD, "/sys/bus/usb/devices/usb2/descriptors", O_RDONLY|O_LARGEFILE|O_CLOEXEC) = 11
[pid 20695] read(11, "\22\1\0\2\t\0\0@k\35\2\0\t\6\3\2\1\1\t\2\31\0\1\1\0\340\0\t\4\0\0\1"..., 256) = 43

...
omasanori commented 2 months ago

I neither have prepared for proper debugging yet, but I found that, ADB_TRACE=all adb server nodaemon on Fedora 40 receives hotplug events for any my USB devices (flash memory, headset, keyboard) but my Fx0, while dmesg showed that it is recognized by the kernel too.

Arnavion commented 2 months ago

I built debug packages of adb and libusb and found the issue.

adb calls:

    rc = libusb_hotplug_register_callback(
            nullptr,
            static_cast<libusb_hotplug_event>(LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED |
                                              LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT),
            LIBUSB_HOTPLUG_ENUMERATE, LIBUSB_HOTPLUG_MATCH_ANY, LIBUSB_HOTPLUG_MATCH_ANY,
            LIBUSB_CLASS_PER_INTERFACE, hotplug_callback, nullptr, nullptr);

where LIBUSB_CLASS_PER_INTERFACE will be used to match the device class. The problem is the PinePhone modem at least does not have this device class, only LIBUSB_CLASS_MISCELLANEOUS, so libusb filters it out.

This patch fixes it:

From d349fae010f6c780d76e89c5d6b81d45119137c0 Mon Sep 17 00:00:00 2001
From: Arnav Singh <me@arnavion.dev>
Date: Sat, 3 Aug 2024 21:55:23 -0700
Subject: [PATCH] Fix libusb enumeration to handle PINE64 PinePhone modem.

The PINE64 PinePhone modem exposes itself as an adb-accessible device over USB
but its device class is LIBUSB_CLASS_MISCELLANEOUS, so allow that too.
---
 vendor/adb/client/usb_libusb.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/vendor/adb/client/usb_libusb.cpp b/vendor/adb/client/usb_libusb.cpp
index 6133e7c8..9af91eb7 100644
--- a/vendor/adb/client/usb_libusb.cpp
+++ b/vendor/adb/client/usb_libusb.cpp
@@ -364,8 +364,8 @@ struct LibusbConnection : public Connection {
     }

     bool FindInterface(libusb_device_descriptor* device_desc) {
-        if (device_desc->bDeviceClass != LIBUSB_CLASS_PER_INTERFACE) {
-            // Assume that all Android devices have the device class set to per interface.
+        if (device_desc->bDeviceClass != LIBUSB_CLASS_PER_INTERFACE && device_desc->bDeviceClass != LIBUSB_CLASS_MISCELLANEOUS) {
+            // Assume that all Android devices have the device class set to per interface or miscellaneous.
             // TODO: Is this assumption valid?
             VLOG(USB) << "skipping device with incorrect class at " << device_address_;
             return false;
@@ -1039,7 +1039,7 @@ void usb_init() {
             static_cast<libusb_hotplug_event>(LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED |
                                               LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT),
             LIBUSB_HOTPLUG_ENUMERATE, LIBUSB_HOTPLUG_MATCH_ANY, LIBUSB_HOTPLUG_MATCH_ANY,
-            LIBUSB_CLASS_PER_INTERFACE, hotplug_callback, nullptr, nullptr);
+            LIBUSB_HOTPLUG_MATCH_ANY, hotplug_callback, nullptr, nullptr);

     if (rc != LIBUSB_SUCCESS) {
         LOG(FATAL) << "failed to register libusb hotplug callback";
-- 
2.46.0

Edit: And the reason this is only an issue with libusb backend is that the original "Linux" backend does not try to filter devices based on device class. It just checks every interface of every device looking for the adb interface.

omasanori commented 2 months ago

Indeed, LGL25 is also a device with the 239 (Miscellaneous) device class!

Arnavion commented 2 months ago

(Just to repeat what I wrote in the Alpine issue, I don't plan to talk to Android upstream about this bug or the patch myself. omasanori brought it up in https://issuetracker.google.com/issues/341439962#comment3 )

nmeum commented 1 month ago

I don't plan to talk to Android upstream about this bug or the patch myself.

Should we just include this patch in our downstream patchset for the time being then? I personally wouldn't mind doing so.

omasanori commented 1 month ago

It would be nice.

fabiensanglard commented 1 month ago

I don't plan to talk to Android upstream about this bug or the patch myself.

(Upstream maintainer here) Sorry about this oversight. The issue has been reported here and we will work on a fix ASAP. It will ship in 35.0.3.