diff --git a/clients/android/app/src/main/AndroidManifest.xml b/clients/android/app/src/main/AndroidManifest.xml index 932c3d6c..1bc78c58 100644 --- a/clients/android/app/src/main/AndroidManifest.xml +++ b/clients/android/app/src/main/AndroidManifest.xml @@ -13,6 +13,12 @@ reception needs it (also an OEM Wi-Fi power-save hedge). --> + + diff --git a/clients/android/app/src/main/kotlin/io/unom/punktfunk/StreamScreen.kt b/clients/android/app/src/main/kotlin/io/unom/punktfunk/StreamScreen.kt index 8dc852f3..fd91c1c8 100644 --- a/clients/android/app/src/main/kotlin/io/unom/punktfunk/StreamScreen.kt +++ b/clients/android/app/src/main/kotlin/io/unom/punktfunk/StreamScreen.kt @@ -6,6 +6,7 @@ import android.content.pm.ActivityInfo import android.content.pm.PackageManager import android.net.wifi.WifiManager import android.os.Build +import android.util.Log import android.view.SurfaceHolder import android.view.SurfaceView import android.view.WindowManager @@ -126,8 +127,10 @@ fun StreamScreen(handle: Long, micEnabled: Boolean, onDisconnect: () -> Unit) { // - FULL_HIGH_PERF covers older releases — it is deprecated AND a documented no-op on recent // Android, which is exactly why it can't be the only lock (a lesson learned: holding just // HIGH_PERF left power save fully active on Android 13+). - // Needs no permission beyond ACCESS_WIFI_STATE (declared). Non-reference-counted: one explicit - // acquire/release each. + // acquire() ENFORCES the WAKE_LOCK permission (manifest) — and a failed acquire MUST be loud: + // a silent runCatching hid the missing permission for weeks (dumpsys wifi showed + // low_latency_active_time_ms=0 across every "locked" stream). Non-reference-counted: one + // explicit acquire/release each. val wifiLocks = remember(handle) { val wm = context.applicationContext.getSystemService(Context.WIFI_SERVICE) as? WifiManager ?: return@remember emptyList() @@ -144,7 +147,11 @@ fun StreamScreen(handle: Long, micEnabled: Boolean, onDisconnect: () -> Unit) { DisposableEffect(handle) { window?.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) - wifiLocks.forEach { runCatching { it.acquire() } } + wifiLocks.forEach { lock -> + runCatching { lock.acquire() }.onFailure { e -> + Log.w("punktfunk", "WifiLock acquire failed — power save stays ON: $lock", e) + } + } // HDMI Auto Low-Latency Mode: ask the display to drop its post-processing (game mode) — // the biggest panel-side latency win on the TV boxes. No-op where ALLM isn't supported. API // 30+. Part of the experimental low-latency stack.