Sync With Another Phone
Status: Current implemented behavior. Audience: Users and contributors. Implementation:
features/devicesync,features/settings. Navigation:Screen.SettingsDeviceSync; settings sectionDEVICE_SYNC. Related: Feature map, Settings and preferences, Permissions, Privacy.
Sync with another phone copies Health Connect records, medical records among them, directly between two nearby Android phones over Bluetooth. There is no account, no server, and no network step.
It is reached from Settings, Sync with another phone, which opens its own wizard.
The Wizard
- Choose a role. One phone makes itself discoverable and becomes the host. The other looks for a phone and becomes the guest.
- Pair. The host makes itself discoverable and waits. The guest scans and picks the host from the list of nearby phones. Already-paired phones appear in the list before the scan finishes.
- Choose how far back. The last 30 days, the last 6 months, the last year, or everything. The last year is the default.
- Choose what to sync. The picker lists data categories such as activity, workouts, heart, sleep, body measurements, vitals, nutrition, hydration, mindfulness, cycle tracking, and medical records. A category appears only when this phone can both read and write at least one of its record types, and everything supported is selected by default. Medical records ignore the range, and the picker says so. Cycle tracking also carries the day-log journal, the excluded cycles, the contexts and age band, the pill scheme, and the taken days: a day edited on both phones keeps the later edit, taken days merge, and reminder settings stay on their phone.
- Compare the codes. Once both users have pressed Start, both phones show a six-digit code. Each user says whether they match. Nothing is exchanged before both did, and “They do not match” ends the session with nothing sent. The first phone to press Start waits up to ten minutes for the other user.
- Sync. Progress shows the current phase and live sent, received, and written counts.
- Read the report. The report shows how many records were merged, how many were already present, and a per-record-type breakdown of what arrived. It can be copied or shared as text. A failed sync gets the same treatment: the screen shows why the session aborted and how far it got, with the same copy and share buttons, and the last report is offered again when the wizard is reopened.
Records are streamed from Health Connect in pages rather than loaded all at once, so a library holding years of dense data - a bike computer’s worth of heart rate samples - syncs within the memory of a small phone.
Both phones choose their own range and their own categories. The exchange uses the record types both phones support.
Both Directions At Once
The exchange is bidirectional within a single session. Both phones send and receive over the same connection at the same time, and each phone reports what it wrote.
The category selection controls what this phone sends. What it receives is decided by the other phone’s selection, which is why the picker is framed as what to accept and shows the types both phones agreed on.
Bluetooth, Not The Internet
The transfer runs over Bluetooth Classic RFCOMM on a private OpenVitals service identifier, so the app only ever connects to another OpenVitals.
This is a deliberate choice rather than a convenience. Any Wi-Fi or TCP socket on Android requires the INTERNET permission, which OpenVitals does not declare and actively removes from the manifest. Bluetooth Classic needs no such permission, so the transfer stays peer-to-peer with no network involved. See Permissions.
The host phone must be made discoverable, which Android asks about with its own dialog. Connecting to a phone for the first time triggers Android’s standard pairing dialog; that bond is what encrypts the link.
What The Pairing Code Protects
The two phones agree a fresh session key over the Bluetooth link: the host commits to a key, the guest sends its own, and only then does the host reveal. Both phones derive the same six digits from that exchange and show them, so the code is compared by the two users, never typed. Someone in the middle holds a different key with each phone and the codes differ; the commitment stops a search for a key that gives the same code. Every frame after the key exchange is sealed, so a changed, replayed, dropped or reordered frame ends the session. Both phones need 2.10.0 or later; an older version is refused at the handshake.
Re-Syncing Does Not Duplicate
Each record is identified by a fingerprint computed from its own content: the record type, its timestamps, and its values. Both phones compute the same fingerprint for the same logical record.
- Before exchanging, each phone fingerprints what it already has. An incoming record it already holds is counted as already present and is not written.
- Records that are written carry their fingerprint as the Health Connect client record ID, so Health Connect updates in place rather than duplicating.
- A receiving phone re-derives the fingerprint from the record it decoded rather than trusting the identifier the other phone sent, so a sync cannot overwrite an unrelated record.
Running the same sync twice therefore writes nothing new, and the two phones converge.
Medical Records
The medical records category carries the FHIR records in Health Connect. It appears when this phone may write medical records and read at least one medical category. Sync never asks for these permissions: the Medical records area asks the first time it opens.
- Medical records always sync in full, whatever range is chosen. A vaccination history cut at one year is not useful.
- Each record travels as it would in a FHIR export, with its source’s address, name, and FHIR version. The receiving phone files it into its own source for the same origin, making one when needed, and runs the import checks. A source another app keeps on the receiving phone is left alone, as in an import.
- A record is identified by its source, type, id, and content. An unchanged record counts as already present. When the phones hold two versions of one record, the one with the later edit time (
meta.lastUpdated) wins, and a version without one never replaces the other. Records typed in OpenVitals carry that time. - Patient records go first. When they name someone other than the people on the receiving phone, or more than one person, that phone keeps none of the medical records from this sync, and the report says why. To move them anyway, export them on one phone and import them on the other, where the import checks the person with the user.
- Saved documents stay on their phone.
- The report’s medical line counts records added, already here, skipped, and not added.
See Medical records.
While The Sync Runs
A quiet ongoing notification, “Syncing with another phone”, asks the user to keep both phones nearby. It exists so Android does not kill the app while the user is looking at something else.
The transfer belongs to the wizard screen: switching to another app is fine, but navigating away from the wizard inside OpenVitals ends the session. A sync also will not start while an activity recording is running; the recording has to be finished or discarded first.
Permissions
Sync asks for nearby-device Bluetooth permissions, and for location on Android versions before 12 where classic Bluetooth discovery requires it. It then asks for the Health Connect read and write permissions for the record types it can exchange, and finally, on the host, for Android’s discoverable window. It never asks for medical permissions.
Privacy
Records move directly between the two phones over Bluetooth and are written to Health Connect on each device. Nothing is uploaded, and the app has no internet permission to upload with. The most recent sync report is stored locally as a text file in the app’s own storage; only the latest one is kept.