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

--- Comment #3 from Noah Jon <[email protected]> ---
Albert, I'm addressing this to you as the component maintainer: I have a patch
for this that I can't open as a merge request upstream, so I'm posting it here
for review.

I ran into the same problem. A few weeks ago I forked the app and patched it,
and I've been using the patched build since then with no issues. My patch keeps
the lock but bounds how long it is held, instead of removing it.

Device: OnePlus 12 (CPH2573), Android 16, OxygenOS 16.0.10.501. Paired with a
Linux desktop.

Disclosure: I used AI assistants (OpenAI Codex and Anthropic Claude) to help
analyse battery statistics, write the patch and tests, and run the on-device
checks below. I reviewed the changes and the results myself.

FIX

Branch:
https://invent.kde.org/noah/kdeconnect-android/-/tree/fix-battery-multicast-drain

Based on master 4d317ee (1.37.0 Beta):

1. Bound mDNS discovery to a 30 second window
  
https://invent.kde.org/noah/kdeconnect-android/-/commit/202764369c957be891426806019a401dcc803c26
2. Rate limit connection refreshes on screen on
  
https://invent.kde.org/noah/kdeconnect-android/-/commit/f238c4f658cdc751398a19f1c9211a56eaee3cbc

CAUSE

`MdnsDiscovery.startDiscovering()` acquires `kdeConnectMdnsMulticastLock` and
only releases it in `stopDiscovering()`. That runs when the service stops, or
on the next network change (`LanLinkProvider.onNetworkChange()` calls stop and
then start). So the lock lives as long as the discovery session, which in
practice means as long as the phone stays on one network.

A second trigger makes it worse. `KdeConnectBroadcastReceiver` calls
`BackgroundService.ForceRefreshConnections()` on every `ACTION_SCREEN_ON`. Each
refresh sends a UDP identity broadcast and restarts mDNS discovery.
`DELAY_BETWEEN_BROADCASTS` (200 ms) in `LanLinkProvider` does not limit this in
practice.

WHAT THE PATCH DOES

1. `startDiscovering()` schedules `stopDiscovering()` 30 s after it starts,
which releases the lock. The lock is also released from `onDiscoveryStopped`
and `onStartDiscoveryFailed`, and callbacks from an already stopped session are
ignored. `MainActivity.onStart()` calls `ForceRefreshConnections()`, so opening
the app opens a new window.
2. The `ACTION_SCREEN_ON` refresh is skipped when every paired device is
already reachable. Otherwise it runs at most once per 5 minutes. Network
changes and opening the app still refresh immediately.

Robolectric tests are included (`MdnsDiscoveryTest`,
`KdeConnectBroadcastReceiverTest`). The full unit test suite passes (144
tests).

MEASUREMENTS (BATTERYSTATS, KDE CONNECT UID)

With patch 1 only, over about 13 h on battery (about 11 h of that with the
screen off):

- WiFi multicast wakelock: 57 acquisitions, 19 min 50 s in total, each about 30
s.
- Every lock session lined up with a screen-on. There were none while the
screen stayed off.
- CPU 7.9 s, no partial wakelocks. KDE Connect did not appear among the apps
causing network wakeups.

For comparison, the reporter above saw 16 h 38 min with the unpatched build.

With patch 2 added, three screen-on events 6 s apart produced one 30 s
discovery session (`durationMs=30023` in `dumpsys wifi`). The other two logged
`Screen on: refreshed recently, skipping refresh`.

Already paired devices stay connected over TCP, so the window does not affect
them. Discovering a new device needs one of: opening the app, a network change,
or a screen-on refresh.

On the question in the original report: removing the lock entirely might work
on some devices, but whether NsdManager receives multicast without it depends
on the vendor's APF/offload implementation. Bounding the lock keeps discovery
working everywhere and still lets the device return to multicast filtering most
of the time.

I'm happy to rework the patch if you prefer a different approach, for example a
different window length or making the screen-on behaviour configurable.

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

Reply via email to