https://bugs.kde.org/show_bug.cgi?id=525625
Bug ID: 525625
Summary: Contacts: response_uids_timestamps sends timestamps as
JSON strings instead of numbers
Classification: Applications
Product: kdeconnect
Version First unspecified
Reported In:
Platform: Android
OS: Android 17.x
Status: REPORTED
Severity: normal
Priority: NOR
Component: android-application
Assignee: [email protected]
Reporter: [email protected]
CC: [email protected]
Target Milestone: ---
DESCRIPTION
The kdeconnect.contacts.response_uids_timestamps packet has been sending
contact modification timestamps as JSON strings instead of JSON numbers since
December 2024. This is a regression introduced during the Kotlin migration of
the Android ContactsPlugin, and it is still present on master.
KDE's own schema in kdeconnect-meta
(schemas/kdeconnect.contacts.response_uids_timestamps.json, on the
work/protocol-schemas branch) types the field explicitly:
"additionalProperties": {
"type": "number",
"description": "Contact modification timestamp (ms)."
}
kdeconnect-kde is unaffected for reasons given below, so this is invisible from
the KDE desktop side. Clients that read the field as a number are affected, and
in at least one case contacts stop syncing entirely with no error reported.
STEPS TO REPRODUCE
1. Pair the Android app with any client.
2. Trigger a contacts sync so the device sends
kdeconnect.contacts.response_uids_timestamps.
3. Inspect the packet body.
OBSERVED RESULT
Timestamps are quoted strings:
{"<uid>": "1735401861000", ...}
EXPECTED RESULT
Timestamps are numbers, as the schema requires and as emitted prior to
December 2024:
{"<uid>": 1735401861000, ...}
SOFTWARE/OS VERSIONS
KDE Connect Android: 1.35.13
Operating System: Fedora Silverblue
Plasma, Frameworks and Qt versions are not applicable: the defect is in the
Android application, and this desktop does not run Plasma.
ADDITIONAL INFORMATION
Where it was introduced
Commit 2d8b43d75d113f4d0b94edc02dc8c7a035183077 ("Migrate ContactsPlugin to
Kotlin (conflict resolution)", 2024-12-28). The relevant hunk in
handleRequestAllUIDsTimestamps:
- reply.set(contactID.toString(), timestamp);
+ set(contactID.toString(), timestamp.toString())
Why this changes the output type
In the Java version timestamp was a boxed Long. No reference-type overload of
NetworkPacket.set accepts a java.lang.Long (those candidates are String,
JSONArray, JSONObject, Set and List), so Java overload resolution unboxed it
and selected set(String, long), whose body is mBody.put(key, value) and which
therefore wrote a JSON number.
The Kotlin version passes timestamp.toString(), which selects
set(String, String) and writes a quoted string instead.
The set(key: String, value: Long) overload still exists in NetworkPacket.kt, so
the original behaviour is available; it is simply no longer being reached.
Current location
src/main/java/org/kde/kdeconnect/plugins/contacts/ContactsPlugin.kt, in
handleRequestAllUIDsTimestamps:
for ((contactID: uID, timestamp: Long) in uIDsToTimestamps) {
set(contactID.toString(), timestamp.toString())
uIDsAsString.add(contactID.toString())
}
Why kdeconnect-kde is unaffected
Two reasons, which together are presumably why this has gone unnoticed for
roughly twenty months.
First, the templated NetworkPacket::get reads the body through QVariant's
value() conversion, and QVariant converts a QString to qint64, so the coercion
succeeds silently.
Second and more importantly, ContactsPlugin::handleResponseUIDsTimestamps
iterates the uids list and requests any uid whose vCard is missing from local
storage before it ever looks at a timestamp. The timestamp is only consulted to
decide whether an already-cached vCard needs refreshing. A first sync therefore
succeeds regardless of the field's type.
Clients that key the vCard request off the timestamp have no such safety net.
Downstream impact
Valent is a confirmed case. In
valent_contacts_device_handle_response_uids_timestamps
(src/plugins/contacts/valent-contacts-device.c):
int64_t timestamp = 0;
// skip the "uids" array
if G_UNLIKELY (g_str_equal ("uids", uid))
continue;
if G_LIKELY (json_node_get_value_type (node) == G_TYPE_INT64)
timestamp = json_node_get_int (node);
// TODO
if (timestamp != 0)
{
json_builder_add_string_value (builder, uid);
n_requested++;
}
With a string node the type check fails, timestamp stays 0, and every uid is
skipped. n_requested remains 0, so the follow-up
kdeconnect.contacts.request_vcards_by_uid packet is never sent and no contact
is ever synced. No error is raised or logged on either side.
Confirmed against my own devices: GSConnect syncs contacts from this phone
without trouble, while Valent syncs none.
Suggested fix
Pass the Long directly rather than stringifying it, so the numeric overload is
selected again:
set(contactID.toString(), timestamp)
X-KDECONNECT-TIMESTAMP and REV in the vCard path should keep their .toString()
calls, since those are vCard text fields where a string is correct. Only the
packet body field is affected.
--
You are receiving this mail because:
You are watching all bug changes.