diff --git a/kazeia-android/app/src/main/java/com/kazeia/ui/ResourceMonitor.kt b/kazeia-android/app/src/main/java/com/kazeia/ui/ResourceMonitor.kt index aeb5558..67f2bbd 100644 --- a/kazeia-android/app/src/main/java/com/kazeia/ui/ResourceMonitor.kt +++ b/kazeia-android/app/src/main/java/com/kazeia/ui/ResourceMonitor.kt @@ -18,17 +18,12 @@ class ResourceMonitor(private val context: Context) { private var prevIdle = 0L private var prevGpuBusy = 0L private var prevGpuTotal = 0L - private var hasRoot = false - init { - // Test root access once - hasRoot = try { - val p = Runtime.getRuntime().exec(arrayOf("su", "-c", "id")) - val result = p.inputStream.bufferedReader().readText() - p.waitFor() - result.contains("uid=0") - } catch (_: Exception) { false } - } + // No-root deployment (2026-04-14): the previous `su -c id` probe used to + // enable GPU/NPU sysfs reads via root, but it also triggered a Magisk + // prompt on every ChatActivity launch. The whole pipeline now runs in + // the app process so root is never needed — GPU/NPU usage is reported + // as -1 (UI shows "—") and the dashboard shows CPU + RAM only. fun snapshot(): ResourceSnapshot { return ResourceSnapshot( @@ -67,7 +62,9 @@ class ResourceMonitor(private val context: Context) { } private fun readGpu(): Float { - // Try direct read first (works on some devices) + // Non-root path: some devices expose /sys/class/kgsl/kgsl-3d0/gpubusy + // as world-readable. If it's locked down (most SELinux configs do), + // just return -1 — no root fallback, no Magisk prompt. try { val content = File("/sys/class/kgsl/kgsl-3d0/gpubusy").readText().trim() val parts = content.split("\\s+".toRegex()) @@ -81,38 +78,14 @@ class ResourceMonitor(private val context: Context) { if (dt > 0) return (db * 100f / dt).coerceIn(0f, 100f) } } catch (_: Exception) {} - - // Try with root - if (hasRoot) { - try { - val content = execRoot("cat /sys/class/kgsl/kgsl-3d0/gpu_busy_percentage").trim() - val pct = content.replace("%", "").trim().toFloatOrNull() - if (pct != null) return pct.coerceIn(0f, 100f) - } catch (_: Exception) {} - } - return -1f } private fun readNpu(): Float { - // NPU doesn't have a standard busy metric - // Use CDSP (compute DSP) load as proxy if available - if (hasRoot) { - try { - // Check if CDSP is active by reading vote count - val vote = execRoot("cat /sys/bus/platform/devices/soc:qcom,msm-cdsp-rm/cdsp_rm/cpu_vote 2>/dev/null").trim() - if (vote.isNotEmpty()) { - val v = vote.toIntOrNull() ?: 0 - return if (v > 0) 100f else 0f - } - } catch (_: Exception) {} - - try { - // Alternative: check fastrpc activity - val stat = execRoot("cat /proc/fastrpc 2>/dev/null || echo none").trim() - if (stat != "none" && stat.isNotEmpty()) return 50f - } catch (_: Exception) {} - } + // NPU usage reporting required root sysfs reads (cdsp_rm/cpu_vote, + // /proc/fastrpc) that always triggered a Magisk prompt. Removed with + // the no-root migration — no equivalent public API exists, so the + // UI just shows "—" for NPU load. return -1f } @@ -134,12 +107,4 @@ class ResourceMonitor(private val context: Context) { } catch (_: Exception) { return 0 } } - private fun execRoot(cmd: String): String { - return try { - val p = Runtime.getRuntime().exec(arrayOf("su", "-c", cmd)) - val result = p.inputStream.bufferedReader().readText() - p.waitFor() - result - } catch (_: Exception) { "" } - } }