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.

Reply via email to