mirror of
https://github.com/screentinker/screentinker.git
synced 2026-08-14 06:16:20 -06:00
4 commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
1a5c468537 |
fix(#148) android root cause: single-socket-per-device invariant (no duplicate connections)
The player opened duplicate/rapid WebSocket connections for the same device_id: connect() was unconditional (disconnect + forceNew socket) and reachable from every lifecycle entry point (boot, service start, MainActivity/ProvisioningActivity bind, foreground re-bind, START_STICKY). A ROM that re-binds on foreground (MAXHUB PROC_STATE_TOP, isBindService:true) therefore re-invoked connect() repeatedly -> a burst of sockets, each evicted by the next (the 8-in-9s storm). Fire TV never re-binds like that, so it never reproduced. - ConnectionGuard (new, pure/testable — service is the shell, per the OtaThrottle pattern): shouldOpenNewSocket(hasSocket, sameUrl, socketActive) — reuse a live/self-healing socket to the same url; open a new one only when none is usable. - WebSocketService: connect() is now idempotent (@Synchronized + ConnectionGuard) — every entry point reuses the one socket, never opens a duplicate; body split into openSocket(). socketActive / currentUrl track the single socket. - Single owner: onStartCommand now calls connect() so the SERVICE owns the one connection (idempotent across START_STICKY restarts), not whichever activity binds. - Reconnect discipline: on io server/client disconnect (which Socket.IO does NOT auto-reconnect) mark the socket inert and schedule exactly ONE backed-off re-open — never a blind re-open loop; a transport drop keeps socketActive=true so Socket.IO's own reconnect is reused. Test: ConnectionGuardTest (5, incl. 8-rapid-binds-all-reuse). :app:testDebugUnitTest green (ConnectionGuard 5, OtaThrottle 7, ScheduleEval 1). NOT bumped/signed/released — Dan builds+signs with the BMG keystore; 1.9.2-patch2 (server net) covers un-updated devices. |
||
|
|
0c0a8dd68a |
fix(ota): surface stuck OTA on dashboard + read APK signer correctly on API 28/29 (#139)
Follow-up to the cache/backoff loop fix (
|
||
|
|
aa23cf02dd |
fix(ota): stop OTA re-download loop on devices that cannot silently install (#139)
Devices that download an OTA APK but cannot silently install it (Fire TV: no device-owner path) re-downloaded the full APK every check cycle indefinitely - install never completes, version never advances, next check re-triggers. Client (UpdateChecker.kt, ServerConfig.kt, OtaThrottle.kt): - Reuse a cached, signature-verified APK instead of re-downloading every cycle; delete leftover invalid files; keep the verified APK on disk as the manual-install artifact. - Persisted per-version attempt budget (EncryptedSharedPreferences) so it survives the Fire OS app restarts that drive the loop. An attempt is counted only when an install is launched - a download/verify failure does not consume the budget, so a transient network problem cannot park a healthy device in backoff. After 3 failed installs, back off to one retry per 24h. - Clear OTA state and caches when a check returns update_available=false while state is pending (app relaunched as the new version). - Report OTA status to the dashboard via device:log (tag ota) on state transitions only (enter-backoff, clear) to avoid flooding the channel. - Extract throttle decision logic into a pure OtaThrottle object (no Android deps) with JUnit coverage (OtaThrottleTest) for the state transitions. Server (server.js): - Reword /download/apk log from "OTA update in progress" to "APK served" and rate-limit to once per IP / 10 min so a looping device cannot flood the log. Note: client-cooperative fix - prevents the loop in cohorts running this APK. Currently-stuck beta4 devices still require a one-time manual update. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
2ccf3264a9 |
feat(scheduling): per-item schedule blocks (#74 dayparting, #75 auto-expire)
Each playlist item can carry schedule blocks (active days, start/end time-of-day, optional start/end dates). An item plays when the screen's local "now" matches at least one block; an item with no blocks always plays. #74 covers time-of-day/day-of-week windows including overnight wrap; #75 covers inclusive date ranges (auto-expiry). Evaluation is on-device, so dayparting and expiry work offline. - Shared evaluator contract: shared/schedule-vectors.json (39 vectors — DST US+AU, overnight-wrap anchoring, timezone correctness, date boundaries). Canonical JS evaluator in server/lib/schedule-eval.js; Kotlin and Tizen ports kept in lockstep by drift guards (Tizen byte-diff test, Kotlin JUnit reads the shared JSON, new android-test CI job). - All three players (web, Android, Tizen) filter by schedule against their own clock, idle with a "Nothing scheduled" message + 30s re-check when everything is filtered, and fail open on any evaluator error. - Editor: per-item schedule modal + row badge in the playlist editor; client validation mirrors the server; editing marks the playlist draft. - Part B (behaviour change): device/group schedule overrides now evaluate in each device's effective timezone instead of server-local time. - Device detail shows the reported timezone + a clock-skew warning. - i18n for en/es/fr/de/pt across all new strings (namespaced itemsched.* to avoid colliding with the device-schedule calendar's schedule.*). - CHANGELOG documents the feature, the Part B change, the fail-open guarantee, and the scheduled-single-video re-render tradeoff. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |