Skip to content

fix: support upgrading apps from CAMS 1.x to 2.x#579

Merged
Zeroupper merged 4 commits into
mainfrom
backwards-compatibility-1.x
Jun 12, 2026
Merged

fix: support upgrading apps from CAMS 1.x to 2.x#579
Zeroupper merged 4 commits into
mainfrom
backwards-compatibility-1.x

Conversation

@Zeroupper

@Zeroupper Zeroupper commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Problem

Apps upgrading from CAMS 1.x to 2.x hit three independent blockers:

  1. Deserialization. Protocols and deployments created with CAMS 1.x (protocol API level < 2.0) can no longer be deserialized after the device type rename from dk.cachet.carp.common.application.devices.* to dk.carp.cams.devices.*. Old JSON throws a SerializationException and the protocol fails to load entirely.
  2. SQLite schema. Both 1.x and 2.x open carp.db with database version 1, but 2.x renamed the deployment table to studies and added a device_role_name column to task_queue. Since the version never changed, neither onCreate nor any upgrade hook runs on a device with an existing 1.x database — every study save fails with no such table: studies and every task save throws an unhandled no such column: device_role_name exception.
  3. Stored 1.x deployments. Deployment JSON cached on the phone by 1.x omits the deployed timestamp (it was nullable) and uses the 1.x StudyStatus enum names, both of which made SmartphoneDeployment.fromJson throw even with the type aliases in place.

Changes

Deserialization

  • Register the 1.x device type names as fromJson aliases for the 2.x classes — Smartphone, BLEHeartRateDevice, SmartphoneDeviceRegistration, and the devices in the context, health, polar, movesense, esense, and movisens packages.
  • Tolerate missing BLE scan fields (serviceUuids, allowDuplicates) when deserializing BLEDevice configurations, which 1.x JSON does not contain.
  • Restore the 1.x stepcount measure type and probe, removed in 2.x in favor of stepevent.
  • SmartphoneDeployment.fromJson now defaults deployed to now when missing and maps the eleven 1.x StudyStatus names onto StudyDeploymentStatusTypes (e.g. DeployedRunning, deployment-in-progress states → DeployingDevices).

Database migration

  • Bump PersistenceService to database version 2 and add an onUpgrade migration which:
    • creates the studies table,
    • adds the device_role_name column to task_queue and backfills it from the saved task snapshots,
    • copies the mappable columns (ids, roles, timestamps, deployment JSON) from the 1.x deployment table into studies and drops the old table. The 1.x deployment_status column (an enum index) has no 2.x equivalent and is not migrated.
  • The migration inspects the actual schema before changing it, so a database created by 2.x at version 1 passes through unchanged.

Runtime

  • SmartphoneStudyController now reassigns connectedDeviceRegistrations instead of mutating it in place — the map may be the unmodifiable const {} default when no registrations were deserialized from a 1.x deployment.

Testing

  • New api_level_1_compatibility_test.dart in carp_mobile_sensing and the context/polar/movesense packages, deserializing real 1.x JSON.
  • New persistence_service_migration_test.dart which builds a real 1.x database (old schema, a genuine 1.x deployment JSON fixture, a queued task) and verifies that initializing the PersistenceService migrates it: studies and tasks load, the role name is backfilled, and the old table is dropped. Runs on sqflite_common_ffi (new dev dependency).
  • All new and existing suites pass.

Protocols and deployments created with CAMS 1.x (protocol API level
< 2.0) used the carp_core device namespace and could no longer be
deserialized after the rename to the dk.carp.cams.devices namespace.

- register 1.x device type names as fromJson aliases for the 2.x
  classes (Smartphone, BLEHeartRateDevice, SmartphoneDeviceRegistration,
  and the devices in the context, health, polar, movesense, esense,
  and movisens packages)
- tolerate missing BLE scan fields (serviceUuids, allowDuplicates)
  when deserializing BLEDevice configurations
- restore the 1.x stepcount measure type and probe
@Zeroupper Zeroupper force-pushed the backwards-compatibility-1.x branch from b41728c to d7c2b9e Compare June 11, 2026 07:19
@Zeroupper Zeroupper changed the base branch from 1.12.0-perm-hotfix to main June 11, 2026 07:19
@Zeroupper Zeroupper requested a review from bardram June 11, 2026 08:06
@Zeroupper Zeroupper self-assigned this Jun 11, 2026
@Zeroupper Zeroupper added help wanted Extra attention is needed carp_mobile_sensing labels Jun 11, 2026
The map may be the unmodifiable const {} default when no registrations
were deserialized from a 1.x deployment, in which case registering a
connected device throws an UnsupportedError.
Deployments saved by CAMS 1.x omit the deployed timestamp (it was
nullable) and use the 1.x StudyStatus enum names, which made
SmartphoneDeployment.fromJson throw. Default deployed to now when
missing and map the 1.x status names onto StudyDeploymentStatusTypes.
Both 1.x and 2.x opened carp.db with database version 1, so devices
upgrading from a 1.x app kept the old schema: studies were stored in a
'deployment' table and task_queue had no device_role_name column. All
study and task persistence then failed with 'no such table: studies'
and 'no such column: device_role_name'.

Bump the database to version 2 and add an onUpgrade migration which
creates the studies table, adds and backfills the device_role_name
column from the saved task snapshots, and copies the 1.x deployment
table into the studies table. The actual schema is inspected first, so
a 2.x database created at version 1 passes through unchanged.
@Zeroupper Zeroupper changed the title fix: support deserialization of CAMS 1.x protocols and deployments fix: support upgrading apps from CAMS 1.x to 2.x Jun 11, 2026

@bardram bardram left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks very good, incl. the tests.

@bardram

bardram commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Note, however, that you did not touch the

packages.

Are these not affected?

@Zeroupper

Copy link
Copy Markdown
Contributor Author

Note, however, that you did not touch the

packages.

Are these not affected?

That was my first intuition as well. We have two namespaces, one for the device namespace and one for data types. The 1.x break was specifically the device-namespace rename (dk.cachet.carp.common.application.devices.* → dk.carp.cams.devices.*) plus the removed stepcount type. None of apps/audio/communication/connectivity/survey define a device or service configuration, they all sample on the Smartphone primary device

@Zeroupper Zeroupper merged commit 222351f into main Jun 12, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

carp_mobile_sensing help wanted Extra attention is needed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants