VoIPService: acquire a Wi-Fi low-latency lock during calls - #2014
Open
neonlight911 wants to merge 1 commit into
Open
VoIPService: acquire a Wi-Fi low-latency lock during calls#2014neonlight911 wants to merge 1 commit into
neonlight911 wants to merge 1 commit into
Conversation
VoIPService holds a PARTIAL_WAKE_LOCK for the CPU but nothing for the Wi-Fi radio, so the chipset stays in power save for the duration of a call. Downlink packets arriving while the radio dozes are buffered by the AP until the next beacon/DTIM, adding up to ~120ms. The effect is masked while speech is continuous, because the packet flow itself keeps the radio awake. It surfaces in DTX gaps: the first packets after a pause arrive late and the jitter buffer expands, which the user hears as lag that accumulates over the call. Measured on a Xiaomi 12T Pro (Android 15) over 802.11ax 5GHz at -52dBm, pinging the phone from the AP controller at 500ms intervals: no lock, idle avg 56.3ms max ~120ms no lock, in call avg 13.3ms max 192ms lock held, in call avg 3.9ms max 10ms Google Meet already takes this lock (VideoChatWifiLock, type=4) and shows the last row on the same link. Note that WIFI_MODE_FULL_LOW_LATENCY is only effective while the app is in the foreground with the screen on, so this helps speakerphone, video and screen-on voice calls; a call held to the ear with the display off is subject to that platform restriction.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
VoIPServiceacquires aPARTIAL_WAKE_LOCKfor the CPU but nothing for the Wi-Fi radio. The chipset therefore stays in power save for the whole call, and downlink packets that arrive while the radio dozes are buffered by the access point until the next beacon/DTIM — adding up to ~120 ms.While speech is continuous the packet flow itself keeps the radio awake, so the effect is masked. It surfaces in DTX gaps: the first packets after a pause arrive late, the jitter buffer expands, and the user hears lag that accumulates over the call.
Measurements
Xiaomi 12T Pro, Android 15 (SDK 35), 802.11ax 5 GHz at −52 dBm, 573 Mbps, WPA3-SAE. ICMP from the AP controller to the phone, 40 packets per run at 500 ms intervals — the sparse interval is what a speech gap looks like to the Wi-Fi stack.
Google Meet already takes this lock and produces the last two rows on the same link:
Telegram's counter in
dumpsys wifistayed at its baseline across three separate calls (169 → 169, 171 → 171), i.e. no lock is taken in any call mode.The two factors are separable and both are needed: a held-but-ineffective lock (12.0 ms) is equivalent to no lock at all (13.3 ms). The ~120 ms tail is present in every row except the last two. The figure matches one beacon interval (100 TU ≈ 102.4 ms) plus delivery — textbook DTIM buffering of a dozing station.
Ruled out as causes: AP not overloaded (controller CPU 1 %), 0 % loss over 400 packets,
dtim-periodalready 1, beacon interval already at the platform minimum (RouterOS rejects < 100 ms), Androidwifi_power_saveset to 0, and DSCP EF marking makes no difference (62.3 vs 66.5 ms).Why this is the app, not the ROM
A fair objection is that the vendor ROM might be silently dropping a lock request rather than the app never making one. Checked two ways:
1. Bytecode of the installed build. Pulling the shipping APK (12.9.2) and scanning all five DEX files, the string
createWifiLockoccurs exactly once, and the only two classes referencing a Wi-Fi lock are:There is no Wi-Fi lock reference anywhere in the VoIP code path. The single call site is ExoPlayer's media playback, which uses
WIFI_MODE_FULL_HIGH_PERFand is disabled by default — matching the measurement, where0 full high perfwas acquired throughout, so even that one never fired. No request is made from the call path, and nothing can be dropped.2. The ROM honours locks. Meet's lock on the same device appears correctly in
Locks heldwith properworkSourceattribution and yields a 3× latency improvement. If this ROM discarded Wi-Fi locks, Meet's would not work either.Change
wifiLockfield alongside the existingcpuWakelockonCreate()right aftercpuWakelock.acquire(), usingWIFI_MODE_FULL_LOW_LATENCYon API 29+ and falling back toWIFI_MODE_FULL_HIGH_PERFbelowonDestroy()next tocpuWakelock.release(), guarded and null-resetFileLog, so a vendor quirk can't take down a callandroid.permission.WAKE_LOCKandandroid.permission.ACCESS_WIFI_STATEare already declared in the manifest — no manifest change needed.Scope
This is not a legacy-Android issue: the measurements are from Android 15 (SDK 35), and
WIFI_MODE_FULL_LOW_LATENCYis the modern mechanism, introduced in Android 10 as an extension to the Wi-Fi lock API (WIFI_MODE_FULL_HIGH_PERFis the older one, API 12).AOSP documents low-latency mode as active when all of these hold:
— and states the framework explicitly disables power save while the mode is active.
Condition 3 was verified here: with the lock continuously held, turning the screen off moved the average from 4.06 ms to 12.0 ms and the tail back to 184 ms. So the change helps speakerphone, video and screen-on voice calls; a call held to the ear with the display off remains subject to that platform restriction.
Testing status
Please treat this as needing review from someone with the build environment. The patch was written and reviewed against the current
master(base blob0f5e7be), checked for import availability and brace balance, but I have not compiled or run it — I don't have the SDK/NDK toolchain for this project set up. The measurements above come from instrumenting the released build (12.9.2) and comparing it against Google Meet, not from a build of this branch.All measurements are from a single device on a single network. AOSP notes vendors must implement WLAN driver and HAL support for low-latency mode, so the size of the improvement may vary by manufacturer; the missing-lock finding itself is an app code path and is device-independent.