Skip to content

fix(watcher): resume unlock ingest from the unlock bucket's last event - #335

Open
0xbrayo wants to merge 1 commit into
ActivityWatch:masterfrom
0xbrayo:fix/unlock-event-duplicates
Open

0xbrayo wants to merge 1 commit into
ActivityWatch:masterfrom
0xbrayo:fix/unlock-event-duplicates

Conversation

@0xbrayo

@0xbrayo 0xbrayo commented Oct 8, 2026 •

Copy link
Copy Markdown
Member

Part of #333.

SessionEventWatcher used the start of the last stored session as the lower bound for both the session query and the unlock (KEYGUARD_HIDDEN) query. Every run therefore re-sent every unlock since that session started.

Unlocks are sent as heartbeats with pulsetime = 0. The server only merges a heartbeat into the newest event in the bucket, and refuses to merge one older than it, so on each run only the newest unlock merged and every older one in the window was inserted again. Runs happen hourly, on every app open and on widget refresh, so unlock counts kept inflating.

Change

  • getLastEventTime() takes the bucket id.
  • Unlocks resume from 1 ms after the last event in aw-watcher-android-unlock; sessions keep their own cursor.

A fresh unlock bucket still starts from 0, which is correct because nothing has been stored there yet.

Testing

  • ./gradlew :mobile:testStandardDebugUnitTest passes.
  • The changed code is in SessionEventWatcher, which needs a live RustInterface and UsageStatsManager, so this has no new unit test.

Tested on an emulator

Setup: arm64 emulator (API 37, 16 KB pages), debug builds of both versions.

Data was read back through the app's REST API, and imports were triggered with the app's exported LOG_DATA alarm action while another app stayed in front.

The test enabled the swipe lock screen, then did 5 lock/unlock cycles with Settings in front. Each unlock was kept under a second, so no session was recorded between them. It then ran 4 imports. Android's own usage stats confirmed the KEYGUARD_HIDDEN events.

Unlocks performed Unlock events stored Distinct timestamps Duplicates
Baseline 5 11 5 6
Fixed 5 5 5 0

@greptile-apps

greptile-apps Bot commented Oct 8, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 5/5

[Medium impact] The PR appears safe to merge; no new actionable issues were found.

Summary

The PR gives unlock collection its own stored-event cursor.

  • Unlock imports resume after their own latest stored event.

Reviews (3) · Last reviewed commit: "fix(watcher): resume unlock ingest from ..." · Reviewed by Greptile

Comment thread mobile/src/main/java/net/activitywatch/android/watcher/SessionEventWatcher.kt Outdated
@0xbrayo

0xbrayo commented Oct 8, 2026

Copy link
Copy Markdown
Member Author

🤖 Claude, on behalf of @0xbrayo

@greptile review

Unlocks were re-read from the start of the last stored session, so every
run re-sent all unlocks since then. The server only merges a heartbeat
into the newest event in a bucket, so all but the last were inserted
again and unlock counts grew with every ingest run.
@0xbrayo
0xbrayo force-pushed the fix/unlock-event-duplicates branch from 8ea40d1 to f0ecdd5 Compare October 11, 2026 13:13
@0xbrayo

0xbrayo commented Oct 11, 2026

Copy link
Copy Markdown
Member Author

🤖 Claude, on behalf of @0xbrayo

@greptile review

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant