Forward third-party watchapp datalogging to companion apps (classic PebbleKit broadcasts) - #386
Open
Dreamkeeper wants to merge 1 commit into
Open
Forward third-party watchapp datalogging to companion apps (classic PebbleKit broadcasts)#386Dreamkeeper wants to merge 1 commit into
Dreamkeeper wants to merge 1 commit into
Conversation
Datalogging.logData() handled health and system-app tags and silently dropped everything else, so data logged by third-party watchapps (in particular background workers, which cannot use AppMessage and have no other phone-bound channel) never reached their companions - even though the sessions were ACKed. Forward third-party sessions through a new session-scoped CompanionDatalogging interface (opened/data/closed, keyed by watch serial + session id since session ids are connection-local). Android delivers via the classic PebbleKit com.getpebble.action.dl.* ordered broadcasts, split into a platform-independent session/encoding half (ClassicDataloggingSessions, unit-tested) and a thin intent transport: one RECEIVE_DATA per item with a per-session random log UUID, the watch-provided session timestamp, and the session's declared item type (byte-array as Base64, uint as long, int as int - the representations PebbleKit >= 2.6 receivers expect); FINISH_SESSION on close. Data ids are atomic and clock-seeded so process restarts do not replay ids. Payloads that are not a multiple of the item size log a warning and drop only the partial tail. iOS binds a no-op; the JVM platform module is still TODO upstream and is untouched. Delivery is best-effort by design (documented on the interface): items are forwarded as they arrive, nothing is buffered phone-side, and companion ACK_DATA is not consumed. A delivery-guaranteed path would belong in PebbleKit2 as a proper API; this is the minimal bridge. Host tests cover item decoding/encoding, multi-item and partial-tail payload splitting, session metadata stability, per-session UUIDs, close/finish, unknown-session and zero-item-size handling, cross-watch session-id isolation, and data-id uniqueness. Field-tested on a Pebble Time 2 + Android 15: a background worker logging one 14-byte record per minute delivered records through this build (13 in the first session; the watch spools in ~4 min batches), zero with the stock app.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Datalogging.logData()handles health tags and system-app tags, and silently drops everything else — so data a third-party watchapp logs never reaches its companion, even though the phone ACKs the sessions. For background workers this is the only phone-bound channel that exists (workers cannot use AppMessage), so worker-based apps currently cannot get any data off the watch without the user opening the watchapp.Measured on a Pebble Time 2 + this app: a worker logging an item every 60 s (byte-array session, ACKed by the phone) delivered zero items to a registered companion receiver over 10+ minutes.
Change
CompanionDatalogginginterface in commonMain with a session lifecycle (onSessionOpened/onDataItems/onSessionClosed), keyed by watch serial + session id since session ids are connection-local.DataLoggingServicenow passes through the watch-provided session timestamp and declared item type (previously parsed but discarded);Dataloggingforwards non-health, non-system sessions.com.getpebble.action.dl.RECEIVE_DATAordered broadcast per item — same delivery style as the existingPebbleKitClassicAppMessage compatibility — with a random log UUID generated once per session, the real watch timestamp, and each item encoded per its declared type (byte-array → Base64 string extra, uint →longextra, int →intextra).FINISH_SESSIONis emitted on close. Data ids come from a clock-seededAtomicIntegerso concurrent watches and process restarts don't repeat ids. Payloads that aren't a multiple of the item size log a warning and drop only the partial tail.ClassicDataloggingSessions) and a thin intent transport; 13 host tests cover item decoding/encoding, multi-item and partial-tail splitting, session metadata stability, per-session UUIDs, close/finish, unknown-session and zero-item-size handling, cross-watch isolation, and data-id uniqueness.No protocol or storage changes; the phone already ACKs these sessions, this just stops discarding the payloads.
Compatibility notes (deliberate trade-offs, happy to adjust)
long/int) that PebbleKit ≥ 2.6 receivers expect (2.6 replaced GuavaUnsignedIntegerwithlongin the DataLogging API).ACK_DATAis not consumed — if no receiver is running, a record is dropped even though the watch was ACKed. A delivery-guaranteed path would belong in PebbleKit2 as a proper API; this PR is the minimal bridge until then.PebbleKitClassicAppMessage behavior. Two known consequences, flagged as a possible follow-up: any installed app can listen for third-party log data, and manifest-declared receivers on Android 8+ won't wake for them (runtime-registered receivers work, which a worker-based companion needs anyway). Targeting the companion package (intent.setPackage) from PBW/locker metadata would fix both — happy to do it here or in a follow-up, whichever you prefer.Testing
:libpebble3:testAndroidHostTest) on the platform-independent session/encoding logic.Disclosure
Developed with AI assistance (Claude). I reviewed the diff and field-tested it on real hardware, and I understand the code being changed. The use case is a real safety application (unresponsiveness monitor for cryonicists) where worker liveness matters.
🤖 Generated with Claude Code