https://bugs.kde.org/show_bug.cgi?id=523424

Sébastien Beausoleil <[email protected]> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |[email protected]
     Ever confirmed|0                           |1
             Status|REPORTED                    |CONFIRMED

--- Comment #1 from Sébastien Beausoleil <[email protected]> ---
Root cause for this symptom, with a reproducer and a workaround. I can't
confirm it is the original
reporter's cause (their Android version isn't stated), but it produces exactly
this behaviour and
the fix is on our side either way.

**Environment:** GrapheneOS (Android 17, build 2026081301) on a Pixel 8, KDE
Connect Android
1.35.13 (`targetSdk=37`), desktop KDE Connect 25.12.3 on Kubuntu 26.04.

**Cause.** Android 17 stamps a `restricted` column on rows in the SMS provider.
A row written by an
app that does not hold `android.app.role.SMS` gets `restricted=1`, and
`SmsProvider` then filters
those rows out of that app's own reads unless it holds the
`READ_RESTRICTED_MESSAGES` appop, which
defaults to `ignore`. The consequence is circular: **KDE Connect cannot read
back the message it
just wrote itself.** Since the desktop displays only what it can re-read from
the provider, the
reply is invisible there while being genuinely sent and plainly present in the
phone's own
messaging app.

**Reproducer.** Send yourself an SMS from the desktop. The message lands twice
in the provider,
once as `type=2` (sent, written by KDE Connect) and once as `type=1` (received,
written by the
default SMS app). Only the received copy reaches the desktop, seconds later —
proving the link is
healthy and that exactly one row is being withheld:


id: 438
type: 2 sent
creator: org.kde.kdeconnect_tp
restricted: 1
reaches destop: no

id: 439
type: 1 received 
creator: com.android.messaging
restricted: 2
reaches destop: yes


Both rows share the same `thread_id` and address. In this database every
`restricted=1` row — 18 of
them — had `creator=org.kde.kdeconnect_tp` and `type=2`; no other row was
marked.

`logcat` while the desktop requests the thread:

```
V/SmsProvider: checkQueryForForbiddenColumns checking sel: date <= ? AND
thread_id = ?
V/SmsProvider: canReadRestrictedMessages=false
V/MmsProvider: accessRestricted=true
```

**Confirmation.** Granting the appop flips that to
`canReadRestrictedMessages=true`, and every
previously hidden message reappears, including two weeks of history:

```
adb shell appops set --uid org.kde.kdeconnect_tp READ_RESTRICTED_MESSAGES allow
adb shell am force-stop org.kde.kdeconnect_tp
```

UID mode is required — package mode leaves the provider refusing. A restart is
also required, as
the app caches the decision.

**Worth noting:** KDE Connect's `WRITE_SMS` appop is `ignore` (rejected), yet
the row exists
under its name — the platform wrote it on the app's behalf during the send. The
system knows the
message genuinely went out over its own telephony stack, and marks it untrusted
regardless. The
flag tracks *who wrote the row*, not direction: in this database 172 outgoing
messages written
by the Messaging app are unmarked, against 18 written by KDE Connect that are.

**Suggested fix.** The appop is a local workaround, not a solution; it can't be
granted from within
the app. The durable fix is for the Android side to stop depending on a
provider read-back it is
not entitled to: when a send succeeds, emit that message to the desktop
directly from what was just
sent, rather than waiting to observe it reappear in the provider. That also
removes a race that
exists on every Android version, since the read-back is inherently
asynchronous.

If maintainers prefer, the platform behaviour could additionally be raised with
AOSP/GrapheneOS —
an app arguably ought to be able to read rows for which it is itself the
`creator` — but that would
not help existing installs.

-- 
You are receiving this mail because:
You are watching all bug changes.

Reply via email to