Follow-up from the 1.4.2 review (#44, current behavior introduced in ec43809, verified at 53308c9). Posted per the review on #44.
UhidChannel.awaitReady() (app/src/main/java/com/inputleaf/android/shizuku/uhid/UhidChannel.kt:163-178) gates readiness on UHID_OPEN alone:
while (readinessConfig.nanoTime() < openDeadline && !wait.sawOpen.get()) { ... }
if (!wait.sawOpen.get()) return
Kernel mechanics (drivers/hid/uhid.c) — the two events have different guarantees:
UHID_START is queued from uhid_hid_start() (the ll-driver .start callback), invoked by hid_hw_start() during probe, before any userspace node is used. It is guaranteed whenever the device bound.
UHID_OPEN is queued only when a userspace process opens the HID device through the kernel open path:
static int uhid_hid_open(struct hid_device *hid)
{
struct uhid_device *uhid = hid->driver_data;
return uhid_queue_event(uhid, UHID_OPEN);
}
Stock Android's InputReader does open scanned devices, so on AOSP the OPEN arrives — but nothing in the kernel guarantees an opener exists, and the device matrix for this app is precisely OEM ROMs, where input-device open policy is customized (input filters, HID/OTG security toggles, vendor SELinux label variants). An OPEN-less device is a whole-class possibility, not one documented family. (The repo's prior "ColorOS never emits OPEN" rationale does not establish an OPEN-less device: under the corrected constants from #44's first review round, the event that code observed was the real OPEN — mislabeled "START" — and the event it never observed was value 6, UHID_OUTPUT, which never arrives on any device because it is host→device.)
Current behavior regresses attach latency on both device classes:
- OPEN-less devices burn the full
openTimeoutMs on every createDevice() — even though the reader already tracks START (UhidChannel.kt:220) and a log branch exists for it (:127), because the loop condition at :169 checks only sawOpen.
- OPEN-present devices additionally pay the sysfs presence confirmation after OPEN (
:173-178, up to presenceTimeoutMs), where the pre-1.4.2 loop treated presence as an alternative exit, so whichever signal came first won.
Functional impact is bounded: createDevice() logs and proceeds either way, and allowInputLocked() still runs — but every attach eats the timeout(s).
Suggested fix: exit the readiness window on wait.sawStart.get() || wait.sawOpen.get() (START is the kernel-guaranteed readiness signal; OPEN becomes an optional confirmation), demote the sysfs presence check to a logged confirmation or an alternative exit as before, and add virtual-clock tests for both exits through the existing UhidReadinessConfig seams.
Sources: drivers/hid/uhid.c (master): https://raw.githubusercontent.com/torvalds/linux/master/drivers/hid/uhid.c ; ll-driver start-before-connect ordering: hid_hw_start() invokes ll_driver->start prior to hid_connect() in drivers/hid/hid-core.c.
Follow-up from the 1.4.2 review (#44, current behavior introduced in
ec43809, verified at53308c9). Posted per the review on #44.UhidChannel.awaitReady()(app/src/main/java/com/inputleaf/android/shizuku/uhid/UhidChannel.kt:163-178) gates readiness onUHID_OPENalone:Kernel mechanics (
drivers/hid/uhid.c) — the two events have different guarantees:UHID_STARTis queued fromuhid_hid_start()(the ll-driver.startcallback), invoked byhid_hw_start()during probe, before any userspace node is used. It is guaranteed whenever the device bound.UHID_OPENis queued only when a userspace process opens the HID device through the kernel open path:Stock Android's InputReader does open scanned devices, so on AOSP the OPEN arrives — but nothing in the kernel guarantees an opener exists, and the device matrix for this app is precisely OEM ROMs, where input-device open policy is customized (input filters, HID/OTG security toggles, vendor SELinux label variants). An OPEN-less device is a whole-class possibility, not one documented family. (The repo's prior "ColorOS never emits OPEN" rationale does not establish an OPEN-less device: under the corrected constants from #44's first review round, the event that code observed was the real OPEN — mislabeled "START" — and the event it never observed was value 6,
UHID_OUTPUT, which never arrives on any device because it is host→device.)Current behavior regresses attach latency on both device classes:
openTimeoutMson everycreateDevice()— even though the reader already tracks START (UhidChannel.kt:220) and a log branch exists for it (:127), because the loop condition at:169checks onlysawOpen.:173-178, up topresenceTimeoutMs), where the pre-1.4.2 loop treated presence as an alternative exit, so whichever signal came first won.Functional impact is bounded:
createDevice()logs and proceeds either way, andallowInputLocked()still runs — but every attach eats the timeout(s).Suggested fix: exit the readiness window on
wait.sawStart.get() || wait.sawOpen.get()(START is the kernel-guaranteed readiness signal; OPEN becomes an optional confirmation), demote the sysfs presence check to a logged confirmation or an alternative exit as before, and add virtual-clock tests for both exits through the existingUhidReadinessConfigseams.Sources:
drivers/hid/uhid.c(master): https://raw.githubusercontent.com/torvalds/linux/master/drivers/hid/uhid.c ; ll-driver start-before-connect ordering:hid_hw_start()invokesll_driver->startprior tohid_connect()indrivers/hid/hid-core.c.