Conversation
Reviewer's GuideThe PR separates platform-independent connection coordination from Android side effects, adds comprehensive JVM coverage for connection and persistence behavior, and makes preference records IPv6-safe through versioned Base64url encoding with legacy compatibility. Sequence diagram for coordinated connection and input routingsequenceDiagram
participant Service as ConnectionService
participant Coordinator as ConnectionCoordinator
participant State as ConnectionStateMachine
participant Connection as InputLeapConnection
participant Android as AndroidEffects
Service->>Coordinator: beginConnection()
Coordinator-->>Service: generation
Service->>Coordinator: onConnecting(generation, serverIp)
Coordinator->>State: onConnecting(serverIp)
Service->>Connection: connect
Service->>Coordinator: onConnected(generation, serverIp, screenName)
Coordinator->>State: onHandshaking(serverIp)
Coordinator->>State: onIdle(serverIp, screenName)
Connection-->>Service: event
Service->>Coordinator: onEvent(generation, event)
Coordinator-->>Service: Effect
Service->>Android: applyEffects(effects)
alt keepalive timeout
Service->>Coordinator: onKeepAliveMiss(generation)
Coordinator->>State: onDisconnected()
Coordinator-->>Service: CloseConnection, HideCursor, RestoreIme
Service->>Android: applyEffects(effects)
end
Entity relationship diagram for versioned server preference recordserDiagram
PREFERENCES ||--o{ SERVER_RECORD : stores
SERVER_RECORD {
string format "v2"
string server_base64url "IPv6-safe key"
string value_base64url
string legacy_record "optional compatibility input"
}
PREFERENCES {
string tls_fingerprints
string server_transport_modes
}
File-Level Changes
Assessment against linked issues
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
d980aec to
8dd942c
Compare
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
7143fae to
f3a0367
Compare
|
Hello @guaje, I hope you're fine and healthy, i'll be doing some of my changes until you update/reply to this message. So you may have to rebase to master Thanks for your valuable time & efforts. |
|
Hi @anasvhora284,
Thanks for the kind message. I was taking some time off, but I'm back and already finishing the final details of the PRs, starting with PR #40.
Sure thing. I'll rebase if needed. |
|
Hi @guaje,
I didn’t realize you were taking some time off, apologies! Hope you had a great break and got some good rest. 😊 |
37ce4b3 to
cc09c13
Compare
|
Hi @guaje, Hope you're doing well! First of all, thanks for the work you've put into PR #32. The coordinator extraction and the additional test coverage look really good. I wanted to check with you about the merge order between this PR and PR #44 (release/1.4.2). I did a dry-run comparison and found quite a bit of overlap between the two branches:
So, to avoid holding up the 1.4.2 fixes, I was thinking of merging PR #44 into master first and publishing the release, then rebasing PR #32 on top of the updated master. That should give PR #32 a clean baseline to work from and also avoid any unnecessary conflicts around the uhid-server changes that are being removed in #44. Does this order work for you, or would you prefer handling the integration differently? Hoping to get your opinion on this. Thanks again for all the work on this! |
|
Hi @anasvhora284 ,
Thanks for all the work in PR #44! It seems introducing native kernel-level UHID hardware keyboard and mouse emulation was the right call, and you tackled many issues at once.
I completely agree. I'll do a quick review of PR #44, given that neither Sourcery nor Copilot was able to review it. I hope you don't mind.
Yes, I'll rebase this PR once PR #44 gets merged.
Yes, it does, and it completely makes sense.
Thank you for tackling 2 of my issues so cleverly. |
|
Note for the rebase onto post-1.4.2 master — three pieces of decision logic live inline in
Carrying these into #32 avoids filing them as separate upstream issues and avoids reworking #44's new code later. |
|
How are you? I hope everything’s going well on your end! I don’t want to rush you, but whenever you get some free time, could you please share an update on this PR? I’ve also asked you to review PR #46, so whenever you have the time, please take a look at both. No pressure at all! Please take care of yourself and focus on work and life first. I’d appreciate it whenever you’re free and feeling up to it. |
|
Hi @anasvhora284!
Everything is good on my end! Thanks for asking. I hope everything is going well for you, too.
Sure thing! I'll rebase the PR and see whether we can merge it this week.
It'll be my pleasure. |
Thank you so much @guaje |
…artials Narrow the Kover exclusions so ConnectionCoordinator, ConnectionStateMachine, RetryDelayCalculator, and AppPreferences reach the jvm Codecov report: they are plain JVM logic with dedicated unit tests, and excluding them makes the merged patch status count their tested lines as misses. Keep only the Android framework adapters excluded (ConnectionService, CursorOverlayService, NotificationHelper); ConnectionService is reported by the android-coverage emulator job instead. Re-add parsers.jacoco.partials_as_hits so Kotlin inline/lambda mapping artifacts do not defeat the 100% patch target.
ConnectionService is excluded from JVM coverage, so its framework effects are only reportable from the emulator. Add connected tests that bind the real service and play the server half of the Input Leap protocol on the fixed port the service always dials: - full handshake to Active, input routing (mouse abs/rel, key), overlay toggle, abrupt server close, unexpected-disconnect retry, and user disconnect - mid-handshake server close reporting Failed(HANDSHAKE), retry, and cancel - transport failure with a throwing onConnectionFailed observer covering the defensive retry path, with TLS_ONLY proving no retry is scheduled - untrusted TLS certificate rejection without a fingerprint callback - server silence driving the keepalive timeout disconnect The fixtures mirror the JVM LoopbackServer and reuse the app's own SelfSignedRsaCertificate for the TLS listener, so no binary identity needs committing. DataStore is reset through the app's own singleton before the service binds.
The silence test's second wait matched Idle immediately because the state was already Idle after the handshake, so the test passed without letting the keepalive monitor fire its four missed polls — leaving the monitor's disconnect lines uncovered. Require an event well after the initial Idle (client-side close or a retry-caused reconnection) before proceeding.
Matching Idle in the silence wait exits immediately because the connection is Idle after the handshake, so the keepalive monitor never reached its fourth missed poll. Wait for an actual Disconnected transition instead; the close race may reconnect, so extend the deadline to cover a second cycle.
No class reports selectively anymore. The jvm session covers plain JVM logic and the android-coverage emulator session covers the framework adapters; Codecov merges both line-by-line, so the enforced 100% patch gate needs no package/class allow-lists and untested code stays visible.
…cycleTest The old name stutters and reads as a test of a nonexistent ConnectionServiceConnection class. The new name states the subject (ConnectionService) and the behavior under test (its connection lifecycle), matching the name-classes-after-the-subject convention.
cc09c13 to
95c02b4
Compare
95c02b4 to
9752b31
Compare
b7a8e98 to
6d35f92
Compare
caa975f to
b522fe5
Compare
b522fe5 to
a37dae5
Compare
…onConfigurationChanged
a37dae5 to
e892eb7
Compare
Summary
ConnectionCoordinatorwhile retaining Android lifecycle and side effects inConnectionServiceAppPreferencestestable with an isolatedDataStoreand deterministic device namesConnectionServicelifecycle (the one part JVM tests cannot reach) so patch coverage is honest and complete without anycodecov.ymlignore entriesTest design
ConnectionServiceon the emulator and drive it against a loopback Input Leap server on the fixed port the service dials:Failed(HANDSHAKE)→ retry → cancel via disconnectonConnectionFailedobserver exercising the defensive retry path (TLS_ONLY proves no retry is scheduled)LoopbackServerand the app's ownSelfSignedRsaCertificate, so no binary identity is committed; DataStore is reset through the app's own singleton before the service bindsCoverage reporting
after_n_builds: 2), so a line counts as covered when either session hits it. The enforced 100% patch gate applies to every changed line regardless of which session covers it, and untested code stays visible — no package/class allow-lists inbuild.gradle.kts, noignoreentries incodecov.yml.parsers.jacoco.partials_as_hitscounts Kotlin inline/lambda mapping artifacts so the 100% patch target measures genuinely unexecuted lines.ConnectionService's changed lines are exercised by theandroid-coverageemulator job;ConnectionCoordinator,ConnectionStateMachine,RetryDelayCalculator, andAppPreferencesare exercised by the jvm session;MainViewModel/HiddenInputManagerJVM tests (previously hidden by package-wide excludes) report again.ignoreentries and no Kover/build.gradle.ktsexclusions.Validation
git diff --check./gradlew :app:testDebugUnitTest :uhid-server:test(not run locally: no Java Runtime is installed in this environment)fast-jvm✓,android-coverage✓ (connected suite incl.ConnectionServiceLifecycleTest),codecov/patch✓ 100.00% — https://github.com/anasvhora284/input-leaf/actions/runs/35273559965Readiness
upstream/master(after build: platform cluster bump - AGP 9.4, Kotlin 2.4.10, compileSdk 37, version catalog #40/docs: optimize repository for search discovery #41/feat: Shizuku restart recovery, mid-connection auto-reconnect, and in-app update checker #42/ci(codecov): fix master coverage report and UpdateService gaps #43). Two merges worth calling out:ConnectionCoordinatorextraction now coexists with feat: Shizuku restart recovery, mid-connection auto-reconnect, and in-app update checker #42's Shizuku restart-recovery / mid-session auto-reconnect inConnectionService. The coordinator owns connection-generation and disconnect state; feat: Shizuku restart recovery, mid-connection auto-reconnect, and in-app update checker #42's Shizuku binder listeners,setInjectordisconnect callback, andtriggerShizukuRecoveryare preserved verbatim (their guard reads stay byte-identical so they add no patch-coverage burden), and the lifecycle tests still cover the coordinator-ownedconnect/disconnect/onDestroypaths.Closes #12
Part of #8
Summary by Sourcery
Centralize connection decisions and expand JVM and emulator coverage across connection behavior, persistence, and service lifecycle paths.
New Features:
Bug Fixes:
Enhancements:
CI:
Documentation:
Tests: