Correctness and performance issues in watchers, sync and workers
I maintainer di solito rispondono entro 2 giorni
@0xbrayo ci sta già lavorando.
Dal 8/10/2026.
Valutazione
- Difficoltà
- 5/5
- Tempo stimato
- Più di una settimana
- Idoneità per principianti
- 15/100
Direzione di ricerca
This is a tracking issue, not a single task: each checklist item names its own file and fix PR (#334 to #351), so pick one item and go to its named code, such as SessionEventWatcher's SimpleDateFormat parse or SyncScheduler. Start by reading that file and reproducing the described behaviour with its test or a log line. Done means that item's linked fix PR is merged and its checkbox is ticked; the Rust JNI item (#801) depends on an upstream change, so avoid it.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
An audit of the app's watchers, sync, workers and widget found the correctness and performance issues below. Each item will be fixed in its own PR, linked next to it.
Correctness
-
Session watcher replays all history when the last event's timestamp has no fractional seconds. Fix: #334.
SessionEventWatcher(and the legacyUsageStatsWatcherpath) parse the last stored event's timestamp withSimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX"). aw-server-rust (chrono) omits the fraction when it is zero (2026-10-08T10:00:00Z), which throwsParseException. The watcher then treats the bucket as empty, queries usage events from 0, and every replayed session is inserted again because the heartbeat merge refuses events older than the last one. Roughly 1 in 1000 sessions starts on a whole second. -
Unlock events are duplicated on every ingest run. Fix: #335. Unlocks are re-read from the start of the last session rather than from the unlock bucket's own last event. Every run re-sends all unlocks in that window; only the newest merges, the older ones are inserted again, so unlock counts inflate with each hourly alarm, app open and widget refresh.
-
Two datastores can be opened concurrently in the JNI layer. Fix: ActivityWatch/aw-server-rust#801 (needs an
aw-server-rustsubmodule bump here once merged).openDatastore()inaw-server/src/android/mod.rsinitialises an unsynchronisedstatic mut DATASTORE. Several threads call it at startup (WebWatcher, MediaWatcher, SessionEventWatcher, the server thread, migrations), so twoDatastore::newcalls can race, giving two workers with separate SQLite connections and separate heartbeat caches. -
Sessions of 4 hours or more are silently dropped. Fix: #336.
SessionParserdiscards any session at or aboveMAX_REASONABLE_SESSION_DURATION(4h), losing genuine long sessions (navigation, video, reading). The cap exists to guard against missing PAUSE events, but the screen turning off is a better bound. -
The in-progress foreground session is never counted until it ends. Fix: #337. The parser does not emit the trailing open session, so a 2-hour session in one app is invisible to the dashboard, widget and alerts until the user leaves it.
-
Category alerts are evaluated against stale data. Fix: #338.
NotifyWorkerqueries category time without first ingesting new usage events, so alerts lag by up to the hourly (inexact) ingest alarm. -
Cancelling a sync allows a second concurrent sync. Fix: #339.
SyncInterface.cancel()clears the sharedsyncInFlightflag immediately, but interrupting the executor does not stop the nativesyncBoth, so a new sync can start while the old one is still running. -
Sync runs twice per 15-minute interval. Fix: #340.
SyncScheduler's Handler chain and the AlarmManager fallback both fire every 15 minutes;syncInFlightonly prevents overlap, so each cycle can do two full syncs and two SAF mirrors. A stop/start while a sync is in flight can also leave two Handler chains running. -
RustInterface.serverStartedis not volatile. Fix: #341. It is written on the main thread and polled in a loop by the hostname-rewrite thread, which may never observe the change. -
Browser sessions are split by the keyboard and other overlay windows. Fix: #342.
WebWatcherends the session on any event from a different window id, so typing in the browser (IME window) or a popup ends and restarts the visit. -
Media events are not merged when two players are active. Fix: #343. With two media sessions playing, heartbeats for different data alternate in one bucket, so they never merge and each 15s poll inserts new zero-duration events.
-
The app can crash with SIGBUS on first launch. BackgroundService rewrites bucket hostnames in
sqlite.dbthrough Android's SQLite while the Rust datastore, which bundles its own SQLite, may already have the same WAL database open (watchers open it over JNI before the server starts; the only guard isserverStarted). Each library resizes the shared-shmmapping under the other and the process dies withBUS_ADRERRinwalIndexAppend. This is the intermittentTest release variant (R8 smoke)CI failure. Fix: #354.
Performance
- WebWatcher writes events on the accessibility main thread. Fix: #344.
logBrowserEventcalls the blocking JNIheartbeaton the service's main thread, the same pattern that caused thecreateBucketANR in #261. - WebWatcher walks the accessibility tree on every content-change event. Fix: #345. Every browser
typeWindowContentChangedevent runsfindWebView(up to 2000 binder calls), including for Firefox where it can never match. -
BackgroundService.onCreateconstructsRustInterfaceon the main thread. Fix: #346.loadLibrary+ JNI initialise run on the main thread during service creation, which #262 moved off-thread everywhere else. - The SAF mirror rewrites every file on every sync. Fix: #347.
mirrorDirectorycopies all files in full regardless of whether they changed, sotest.dbis rewritten every 15 minutes. - Session ingest does redundant work per run. Fix: #348. One
getApplicationInfobinder call per session (no cache), two full passes overqueryEvents(sessions and unlocks), andcreateBucketHelperserialises every bucket on each of its two calls. - The widget runs the full-day query once per widget instance. Fix: #349.
updateAllWidgetsre-queries and re-reads settings for every placed widget instead of once. - NotifyWorker's preferences grow without bound. Fix: #350. A new
triggered_<category>_<hash>_<date>key is written per alert per day and never removed; SharedPreferences loads the whole file on each access. - The share-sheet export fallback loads the whole export into memory on the UI thread. Fix: #351.
shareExportcallsreadContent(), undoing the streaming from #304 on that path.
- Lingua principale
- Kotlin
- Stelle
- 279
- Fork
- 63
- Merge medio
- 4g 22h
- PR unite (30g)
- 57
Preparare l'ambiente
Questo progetto non fornisce container di sviluppo, Dockerfile né guida per i contributori, quindi l'ambiente è a tuo carico: parti dal suo README e consulta la nostra guida al primo contributo per i passaggi generali.
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di ActivityWatch/aw-android
-
Web watcher: add support for Brave (`com.brave.browser` missing from KNOWN_BROWSER_PACKAGES)Forse già presa Una pull request collegata a questa issue è aperta o già unita. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
ActivityWatch/aw-android#306 · 1 commento · 1 reazione ·
I maintainer di solito rispondono entro 2 giorni
-
AuthSettingsActivity crashes on launch (InflateException: Material attributes under AppCompat theme)Forse già presa Una pull request collegata a questa issue è aperta o già unita. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 75/100
ActivityWatch/aw-android#210 · 11 commenti · 1 reazione ·
I maintainer di solito rispondono entro 2 giorni
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 54/100
ActivityWatch/aw-android#378 ·
I maintainer di solito rispondono entro 2 giorni
-
Incorrect hostname in `/api/0/info` breaks "this device" badge in Web UIForse già presa Una pull request collegata a questa issue è aperta o già unita. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 35/100
ActivityWatch/aw-android#368 · 1 commento ·
I maintainer di solito rispondono entro 2 giorni
-
CI: emulator jobs (Test E2E, R8 smoke) fail non-deterministically — red run gives no code signalAperta
Difficoltà 4/5 3-5 giorni Idoneità per principianti 30/100
ActivityWatch/aw-android#332 · 1 commento ·
I maintainer di solito rispondono entro 2 giorni
Tutte le issue di ActivityWatch/aw-android
Issue simili
-
afk-ok area:data-quality importer size:S
Difficoltà 2/5 1-3 ore Idoneità per principianti 75/100
enorm-labs/event-junkie#3767 ·
I maintainer di solito rispondono entro 1 giorno
-
bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 82/100
leboncoin/spark-android#2342 ·
I maintainer di solito rispondono entro 1 giorno
-
The Voraton URL has been changedForse già presa Una pull request collegata a questa issue è aperta o già unita. ApertaBug Domain changed
Difficoltà 2/5 1-3 ore Idoneità per principianti 62/100
keiyoushi/extensions-source#20257 · 1 commento · 1 reazione ·
I maintainer di solito rispondono entro 1 giorno
-
bug
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 85/100
I maintainer di solito rispondono entro 1 giorno
-
feature
Difficoltà 2/5 1-3 ore Idoneità per principianti 62/100
user-grinch/RivoPhoneApp#302 ·
I maintainer di solito rispondono entro 1 giorno