diff mbox series

[meta-oe,05/10] android-tools: adbd: signal readiness once functionfs is serviced

Message ID 20261110-android-tools-adbd-v1-5-cc2176f381c3@gmail.com
State New
Headers show
Series android-tools: adbd fixes and opt-in access control for Linux systems | expand

Commit Message

Alex Kiernan Nov. 10, 2026, 5:35 p.m. UTC
adbd sent sd_notify(READY=1) from adbd_main(), but usb_init() has only
started the USB thread by then, so READY=1 raced open_functionfs().
10-adbd-configfs.conf binds the UDC from ExecStartPost with no delay on
the strength of READY=1, so a bind that won the race was dropped and the
host never reached CONFIGURED. Signal readiness from the USB thread once
functionfs is serviced, which is what that drop-in assumes.

Built without systemd, the same point writes a byte to the FIFO named in
$ADBD_READY_FIFO instead, if set, for other init systems. It opens the
FIFO non-blocking, so adbd never waits on a reader that has given up.

AI-Generated: Claude Opus 5.5 (Claude Code)
Signed-off-by: Alex Kiernan <alex.kiernan@gmail.com>
---
 ...nal-readiness-once-functionfs-is-serviced.patch | 117 +++++++++++++++++++++
 .../android-tools/android-tools_35.0.2.bb          |   1 +
 2 files changed, 118 insertions(+)
diff mbox series

Patch

diff --git a/meta-oe/recipes-devtools/android-tools/android-tools/0020-adbd-signal-readiness-once-functionfs-is-serviced.patch b/meta-oe/recipes-devtools/android-tools/android-tools/0020-adbd-signal-readiness-once-functionfs-is-serviced.patch
new file mode 100644
index 000000000000..77527437a4e6
--- /dev/null
+++ b/meta-oe/recipes-devtools/android-tools/android-tools/0020-adbd-signal-readiness-once-functionfs-is-serviced.patch
@@ -0,0 +1,117 @@ 
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Alex Kiernan <alex.kiernan@gmail.com>
+Date: Fri, 6 Nov 2026 10:03:09 +0000
+Subject: [PATCH] adbd: signal readiness once functionfs is serviced
+
+The gadget's UDC must not be bound until adbd is servicing functionfs:
+bind earlier and the bind is silently dropped, and the host never
+reaches CONFIGURED. adbd_main() sends sd_notify(READY=1) after
+usb_init(), but usb_init() only starts usb_ffs_open_thread, detached, so
+READY=1 races open_functionfs() - and a unit that binds the UDC on
+READY=1 (ExecStartPost with no delay) loses the race.
+
+Signal readiness from the USB thread instead, once the descriptors are
+written and the transport is registered. Built without systemd, the
+same point writes a byte to the FIFO named in $ADBD_READY_FIFO instead,
+if set, for other init systems. The FIFO is opened non-blocking, so
+adbd never waits on a reader that has given up. Readiness is signalled
+once; an adbd that is not on USB still signals it from adbd_main().
+
+Upstream-Status: Pending
+
+AI-Generated: Claude Opus 5.5 (Claude Code)
+Signed-off-by: Alex Kiernan <alex.kiernan@gmail.com>
+---
+ packages/modules/adb/adb.h           |  6 ++++++
+ packages/modules/adb/daemon/main.cpp | 31 +++++++++++++++++++++++++---
+ packages/modules/adb/daemon/usb.cpp  |  7 +++++++
+ 3 files changed, 41 insertions(+), 3 deletions(-)
+
+diff --git a/packages/modules/adb/adb.h b/packages/modules/adb/adb.h
+index b43bb42ec736..20c002051937 100644
+--- a/packages/modules/adb/adb.h
++++ b/packages/modules/adb/adb.h
+@@ -263,3 +263,9 @@ void adb_set_reject_kill_server(bool reject);
+ #endif
+ 
+ void usb_init();
++#if !ADB_HOST
++// Tell whoever started adbd that it is ready: sd_notify(READY=1) with systemd,
++// otherwise a byte to the FIFO named in $ADBD_READY_FIFO if set. Only the first
++// call does anything.
++void adbd_notify_ready();
++#endif
+diff --git a/packages/modules/adb/daemon/main.cpp b/packages/modules/adb/daemon/main.cpp
+index ac37461f21dc..a5b1d6b3e0eb 100644
+--- a/packages/modules/adb/daemon/main.cpp
++++ b/packages/modules/adb/daemon/main.cpp
+@@ -53,6 +53,8 @@
+ #include <systemd/sd-daemon.h>
+ #endif
+ 
++#include <mutex>
++
+ #include "adb.h"
+ #include "adb_auth.h"
+ #include "adb_listeners.h"
+@@ -244,6 +246,27 @@ static void drop_privileges(int) {
+ }
+ #endif
+ 
++void adbd_notify_ready() {
++    static std::once_flag once;
++    std::call_once(once, [] {
++#if defined(HAVE_SYSTEMD)
++        sd_notify(1, "READY=1");
++#else
++        const char* ready_fifo = getenv("ADBD_READY_FIFO");
++        if (ready_fifo != nullptr && *ready_fifo != '\0') {
++            // O_NONBLOCK: with no reader, open() fails with ENXIO rather than
++            // blocking this thread for ever; the reader has given up and
++            // gone ahead without the signal.
++            int fd = adb_open(ready_fifo, O_WRONLY | O_NONBLOCK | O_CLOEXEC);
++            if (fd >= 0) {
++                TEMP_FAILURE_RETRY(adb_write(fd, "1\n", 2));
++                adb_close(fd);
++            }
++        }
++#endif
++    });
++}
++
+ static void setup_adb(const std::vector<std::string>& addrs) {
+ #if defined(__ANDROID__)
+     // Get the first valid port from addrs and setup mDNS.
+@@ -368,9 +391,11 @@ int adbd_main(int server_port) {
+     init_jdwp();
+     D("adbd_main(): post init_jdwp()");
+ 
+-#if defined(HAVE_SYSTEMD)
+-    sd_notify(1, "READY=1");
+-#endif
++    // On USB, readiness is signalled from the USB thread once functionfs is
++    // serviced; usb_init() has only started that thread.
++    if (!is_usb) {
++        adbd_notify_ready();
++    }
+ 
+     D("Event loop starting");
+     fdevent_loop();
+diff --git a/packages/modules/adb/daemon/usb.cpp b/packages/modules/adb/daemon/usb.cpp
+index e44c7b788587..a970c4e1fd28 100644
+--- a/packages/modules/adb/daemon/usb.cpp
++++ b/packages/modules/adb/daemon/usb.cpp
+@@ -805,6 +805,13 @@ static void usb_ffs_open_thread() {
+                 std::move(control), std::move(bulk_out), std::move(bulk_in),
+                 std::move(destruction_notifier)));
+         register_transport(transport);
++
++        // functionfs descriptors are written and ep0 is being serviced, so the
++        // gadget will now handle FUNCTIONFS_BIND: this is the moment the UDC
++        // may be bound. Binding earlier races this setup, and the bind is
++        // silently dropped.
++        adbd_notify_ready();
++
+         future.wait();
+     }
+ }
diff --git a/meta-oe/recipes-devtools/android-tools/android-tools_35.0.2.bb b/meta-oe/recipes-devtools/android-tools/android-tools_35.0.2.bb
index e247cbbafa60..f5ab21262ce5 100644
--- a/meta-oe/recipes-devtools/android-tools/android-tools_35.0.2.bb
+++ b/meta-oe/recipes-devtools/android-tools/android-tools_35.0.2.bb
@@ -64,6 +64,7 @@  SRC_URI = "https://deb.debian.org/debian/pool/main/a/android-platform-tools/andr
            file://0017-adbd-shell-start-in-HOME-and-drop-the-Android-only-T.patch \
            file://0018-adbd-do-not-use-pthread_kill-as-a-thread-liveness-pr.patch \
            file://0019-adb-daemon-exit-after-adb-root-unroot-so-an-init-sup.patch \
+           file://0020-adbd-signal-readiness-once-functionfs-is-serviced.patch \
            "
 
 SRC_URI[orig.md5sum] = "352376965cdef7bd7505d8fefdd43d50"