Skip to content

fix(android): queue notification events until node connect#91667

Closed
mushuiyu886 wants to merge 1 commit into
openclaw:mainfrom
mushuiyu886:feat/issue-79552
Closed

fix(android): queue notification events until node connect#91667
mushuiyu886 wants to merge 1 commit into
openclaw:mainfrom
mushuiyu886:feat/issue-79552

Conversation

@mushuiyu886

Copy link
Copy Markdown
Contributor

Summary

  • Fixes the Android node gateway handshake window so node.event cannot be sent before the connect RPC has succeeded.
  • Adds a bounded queue for failed notifications.changed events and flushes it after node reconnect.
  • Reconciles active Android notifications after listener/node reconnect so missed posted notifications are replayed without duplicating already emitted keys.

Real behavior proof

  • Behavior or issue addressed: Android notification forwarding could emit notifications.changed as node.event while the WebSocket connect RPC was still pending, making the gateway reject the session because the first accepted request was not the completed connect handshake. Reconnect windows could also drop notification events because failed sends were not queued and active notifications were not replayed.

  • Why it matters / User impact: Android users could miss notification forwarding events after app start or reconnect, and the node session could enter an invalid handshake state instead of recovering cleanly.

  • Real environment tested: Local Linux Android JVM/Robolectric environment using the production Android GatewaySession, NodeRuntime, and DeviceNotificationListenerService code paths with OkHttp WebSocket + MockWebServer gateway. ANDROID_HOME=$HOME/.android-sdk; JAVA_HOME=$HOME/.jdks/jdk-21.0.7+6.

  • Exact steps or command run after this patch: From apps/android: ANDROID_HOME="$HOME/.android-sdk" ANDROID_SDK_ROOT="$HOME/.android-sdk" JAVA_HOME="$HOME/.jdks/jdk-21.0.7+6" ./gradlew --no-daemon :app:testPlayDebugUnitTest --tests ai.openclaw.app.gateway.GatewaySessionInvokeTest --tests ai.openclaw.app.node.DeviceNotificationListenerServiceTest --rerun-tasks

  • Evidence after fix: Evidence log: openclaw-issue-79552-evidence/android-focused-tests-after-commit.log

    > Task :app:testPlayDebugUnitTest
    BUILD SUCCESSFUL in 3m 13s
    30 actionable tasks: 30 executed
    
  • Observed result after fix: The focused JVM run exercised the production Android WebSocket session and notification listener paths. The new gateway regression verifies sendNodeEvent() returns false while the connect response is pending and the server only observes connect; the companion regression verifies onConnected can immediately send notifications.changed after connect success. The notification regressions verify bounded queue behavior, failed flush re-prepending, active-notification replay, retry when the sink is unavailable, and no duplicate replay of delivered keys.

  • What was not tested: No physical Android device, OS notification shade, or external OpenClaw gateway was used; the proof is limited to local Android JVM/Robolectric plus local WebSocket gateway behavior.

  • Fix classification: Root cause fix.

Review findings addressed

  • No maintainer review findings existed for this new PR at submission time.
  • ClawSweeper issue criteria were covered locally: focused GatewaySessionInvokeTest, focused DeviceNotificationListenerServiceTest, and git diff --check.

Regression Test Plan

  • Target test file: apps/android/app/src/test/java/ai/openclaw/app/gateway/GatewaySessionInvokeTest.kt and apps/android/app/src/test/java/ai/openclaw/app/node/DeviceNotificationListenerServiceTest.kt.

  • Scenario locked in: The regression tests lock the exact lifecycle scenario: a WebSocket is open, the gateway has received the connect request, the connect response is still pending, and Android notification forwarding attempts to send notifications.changed. They also lock the reconnect scenario where queued notification events flush after connect and active posted notifications replay only when their keys have not already emitted a posted event.

  • Why this is the smallest reliable guardrail: The guardrail lives at the Android node/gateway boundary that was broken. It checks the real GatewaySession.sendNodeEvent() result and the notification replay state instead of asserting implementation details, so future refactors can change internals without reintroducing pre-handshake node.event delivery or duplicate active-notification replay.

  • Android focused JVM tests:

    cd apps/android
    ANDROID_HOME="$HOME/.android-sdk" ANDROID_SDK_ROOT="$HOME/.android-sdk" JAVA_HOME="$HOME/.jdks/jdk-21.0.7+6" ./gradlew --no-daemon :app:testPlayDebugUnitTest --tests ai.openclaw.app.gateway.GatewaySessionInvokeTest --tests ai.openclaw.app.node.DeviceNotificationListenerServiceTest --rerun-tasks
    

    Result: BUILD SUCCESSFUL in 3m 13s; evidence: openclaw-issue-79552-evidence/android-focused-tests-after-commit.log.

  • Android ktlint:

    cd apps/android
    ANDROID_HOME="$HOME/.android-sdk" ANDROID_SDK_ROOT="$HOME/.android-sdk" JAVA_HOME="$HOME/.jdks/jdk-21.0.7+6" ./gradlew --no-daemon :app:ktlintCheck :benchmark:ktlintCheck
    

    Result: BUILD SUCCESSFUL in 22s; evidence: openclaw-issue-79552-evidence/android-ktlint-after-commit.log.

  • Whitespace check:

    git diff --check
    

    Result: passed with no output; evidence: openclaw-issue-79552-evidence/git-diff-check-after-commit.log.

Merge risk

  • Risk labels considered: merge-risk: 🚨 message-delivery, merge-risk: 🚨 availability, merge-risk: 🚨 session-state.
  • What did NOT change: Out of scope: gateway wire format, node.event payload schema, notification action APIs, Android forwarding policy defaults, provider/auth token selection, agent.request, and presence beacon behavior remain unchanged. The patch only changes when the Android node exposes a ready connection and how notifications.changed recovers during reconnect.
  • Architecture / source-of-truth check: The source-of-truth boundary is Android GatewaySession connection lifecycle state plus DeviceNotificationStore active-notification state. This does not add a new public contract, schema, default, migration, or partial override; it restores the existing protocol invariant that the first gateway RPC for a socket is connect. Related open PR scan for issue [Bug]: Android node sends node.event before websocket connect handshake completes, causing notification events to be lost #79552 found no linked open PRs in openclaw-issue-79552-evidence/linked-open-prs-for-issue.json.
  • Risk explanation: The patch changes Android node connection readiness and notification event delivery around reconnect. It deliberately limits queueing to notifications.changed, keeps agent.request and presence beacon failure semantics unchanged, bounds queued notifications to 128 entries, and only promotes a gateway connection when the completed connect RPC still belongs to the current desired target.
  • Why acceptable: The changed behavior is constrained to the broken Android notification forwarding path and handshake exposure boundary. Tests cover the pending-handshake false path, post-connect immediate send path, bounded queue flush/requeue behavior, active notification replay, unavailable sink retry, and duplicate replay prevention.
  • Maintainer-ready confidence: High: the fix is focused to Android gateway/session notification delivery, includes production-path regression tests, and has fresh local evidence collected after the final code changes.
  • Patch quality notes: No dependency, lockfile, public API, schema, protocol format, or configuration default changes. No unrelated files were changed. Patch quality warnings are intentional lifecycle guard/default boundaries: the connectingConnection guard prevents stale handshakes from being promoted, the notification false returns distinguish unavailable sinks from successful emits, and the queue lock is a process-local monitor for the bounded pending notification list rather than a type escape across APIs.

Root Cause

  • Root cause: The Android gateway lifecycle state used currentConnection as both the handshake-stage socket and the ready node socket. That violated the protocol invariant that the first usable gateway RPC on a socket is the successful connect RPC, so notification forwarding could treat a not-yet-connected socket as ready and send node.event during the handshake window. Failed notifications.changed sends were also fire-and-forget, and reconnect did not use active notification state as the source for replaying posted notifications missed while the node connection was unavailable.
  • Why this is root-cause fix: The patch restores the lifecycle state invariant at the source: connectingConnection represents a socket whose connect RPC is still pending, while currentConnection becomes visible only after connect success and before onConnected callbacks run. Notification recovery is tied to the same ready-state transition by queueing only failed notifications.changed events, flushing them after reconnect, and reconciling active notification entries that have not already emitted a posted event. This fixes the invalid first-request source path and the missed-notification recovery path without masking the symptom in downstream gateway error handling or changing unrelated node events.

@openclaw-barnacle openclaw-barnacle Bot added app: android App: android size: M proof: supplied External PR includes structured after-fix real behavior proof. labels Jun 9, 2026
@clawsweeper

clawsweeper Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs real behavior proof before merge. Reviewed June 9, 2026, 4:53 AM ET / 08:53 UTC.

Summary
The PR changes Android gateway readiness so node.event is hidden until connect succeeds, adds a bounded notifications.changed retry queue, replays active notifications after reconnect, and adds focused Android JVM tests.

PR surface: Other +361. Total +361 across 5 files.

Reproducibility: yes. at source level: current main publishes currentConnection before conn.connect() completes, sendNodeEvent can then send node.event, and the gateway rejects non-connect first requests. I did not run a live Android/gateway repro in this read-only review.

Review metrics: 1 noteworthy metric.

  • Queued notification cap: 128 max pending notification events. The bounded queue is the new overload behavior for notifications that arrive while the node connection is unavailable.

Merge readiness
Overall: 🦪 silver shellfish
Proof: 🦪 silver shellfish
Patch quality: 🐚 platinum hermit
Result: blocked until real behavior proof from a real setup is added.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • [P1] Add redacted real Android app or emulator proof against a real OpenClaw gateway showing a notification during reconnect is delivered after connect and no invalid-handshake log appears.

Proof guidance:

  • [P1] Needs real behavior proof before merge: The PR supplies focused JVM/Robolectric and MockWebServer test output, but external PR proof still needs a redacted real Android app/emulator plus real OpenClaw gateway reconnect run; updating the PR body should trigger a fresh ClawSweeper review, or a maintainer can comment @clawsweeper re-review. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.

Risk before merge

  • [P1] External PR proof is limited to Robolectric/MockWebServer tests; it does not show an installed Android app or emulator/device reconnecting to a real OpenClaw gateway while forwarding a notification.
  • [P1] The patch intentionally changes notification delivery around reconnect, so real OS listener timing and gateway reconnect proof matter before merging a message-delivery fix.

Maintainer options:

  1. Require real reconnect proof (recommended)
    Ask the contributor to add redacted logs, terminal output, recording, or screenshots from an installed Android app or emulator reconnecting to a real OpenClaw gateway and delivering a notification after the connect handshake.
  2. Explicitly waive the proof gate
    Maintainers can accept the JVM/Robolectric plus MockWebServer evidence as enough for this Android reliability patch, but that leaves real transport timing unproven.
  3. Pause for maintainer-run Android proof
    If the contributor cannot run the full scenario, leave the PR open until a maintainer can run an Android reconnect proof path and record the result.

Next step before merge

  • [P1] The remaining blocker is contributor or maintainer real behavior proof for an external Android PR; there is no narrow code repair for ClawSweeper to queue from this review.

Security
Cleared: The diff does not change dependencies, CI, secrets, permissions, auth material, or package resolution; notification forwarding remains behind the existing user-enabled Android notification access and forwarding policy.

Review details

Best possible solution:

Land this Android lifecycle fix after adding redacted real Android app plus OpenClaw gateway reconnect proof, while keeping the focused source-level regressions.

Do we have a high-confidence way to reproduce the issue?

Yes at source level: current main publishes currentConnection before conn.connect() completes, sendNodeEvent can then send node.event, and the gateway rejects non-connect first requests. I did not run a live Android/gateway repro in this read-only review.

Is this the best way to solve the issue?

Yes, the proposed source shape appears to be the right owner-boundary fix: keep pending handshakes out of currentConnection, then queue and reconcile notifications.changed after a ready node connection. The remaining gap is proof in a real Android/gateway reconnect setup, not a different code direction.

AGENTS.md: found and applied where relevant.

Codex review notes: model gpt-5.5, reasoning high; reviewed against 257b251e2690.

Label changes

Label changes:

  • add P1: The PR targets a user-facing Android notification-loss path that can prevent notification-triggered automations from waking the agent.
  • add merge-risk: 🚨 message-delivery: The diff changes when Android notification events are queued, replayed, and sent after reconnect, so real message-delivery behavior is the main merge risk.
  • add rating: 🦪 silver shellfish: Overall readiness is 🦪 silver shellfish; proof is 🦪 silver shellfish and patch quality is 🐚 platinum hermit.
  • add status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs real behavior proof before merge: The PR supplies focused JVM/Robolectric and MockWebServer test output, but external PR proof still needs a redacted real Android app/emulator plus real OpenClaw gateway reconnect run; updating the PR body should trigger a fresh ClawSweeper review, or a maintainer can comment @clawsweeper re-review. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.

Label justifications:

  • P1: The PR targets a user-facing Android notification-loss path that can prevent notification-triggered automations from waking the agent.
  • merge-risk: 🚨 message-delivery: The diff changes when Android notification events are queued, replayed, and sent after reconnect, so real message-delivery behavior is the main merge risk.
  • rating: 🦪 silver shellfish: Overall readiness is 🦪 silver shellfish; proof is 🦪 silver shellfish and patch quality is 🐚 platinum hermit.
  • status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs real behavior proof before merge: The PR supplies focused JVM/Robolectric and MockWebServer test output, but external PR proof still needs a redacted real Android app/emulator plus real OpenClaw gateway reconnect run; updating the PR body should trigger a fresh ClawSweeper review, or a maintainer can comment @clawsweeper re-review. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.
Evidence reviewed

PR surface:

Other +361. Total +361 across 5 files.

View PR surface stats
Area Files Added Removed Net
Source 0 0 0 0
Tests 0 0 0 0
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 5 385 24 +361
Total 5 385 24 +361

What I checked:

Likely related people:

  • joshavant: Recent live commit history for GatewaySession.kt and NodeRuntime.kt shows multiple Android gateway/runtime fixes, including pairing retries and provider readiness, near the affected lifecycle surface. (role: recent Android gateway/runtime contributor; confidence: high; commits: 81f4fe6c113b, 72b387ad488c, 53e50ec12747; files: apps/android/app/src/main/java/ai/openclaw/app/gateway/GatewaySession.kt, apps/android/app/src/main/java/ai/openclaw/app/NodeRuntime.kt)
  • steipete: Recent live history for DeviceNotificationListenerService.kt includes adjacent Android notification listener maintenance and comments, making this a useful routing candidate for notification replay semantics. (role: adjacent Android notification/runtime contributor; confidence: medium; commits: 85beee613c64, 694ca50e9775; files: apps/android/app/src/main/java/ai/openclaw/app/node/DeviceNotificationListenerService.kt)
  • hclsys: GitHub search found a closed prior PR for the same linked issue and title shape, so this person may have context on earlier attempted repairs even though that branch did not land. (role: related prior fix proposer; confidence: low; files: apps/android/app/src/main/java/ai/openclaw/app/gateway/GatewaySession.kt)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@clawsweeper clawsweeper Bot added rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P1 High-priority user-facing bug, regression, or broken workflow. merge-risk: 🚨 message-delivery 🚨 May drop, duplicate, misroute, suppress, or wrongly target messages. labels Jun 9, 2026
@mushuiyu886

Copy link
Copy Markdown
Contributor Author

Closing this PR because ClawSweeper requires real Android app/emulator plus real gateway proof, and I cannot provide that proof from the current local environment.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

app: android App: android merge-risk: 🚨 message-delivery 🚨 May drop, duplicate, misroute, suppress, or wrongly target messages. P1 High-priority user-facing bug, regression, or broken workflow. proof: supplied External PR includes structured after-fix real behavior proof. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. size: M status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant