diff --git a/app/src/main/java/com/droiddeck/launcher/runtime/LinuxRuntime.java b/app/src/main/java/com/droiddeck/launcher/runtime/LinuxRuntime.java index 816d7c3ad..b04548b00 100644 --- a/app/src/main/java/com/droiddeck/launcher/runtime/LinuxRuntime.java +++ b/app/src/main/java/com/droiddeck/launcher/runtime/LinuxRuntime.java @@ -193,6 +193,8 @@ public final class LinuxRuntime { } } bindGpuNode(context, cmd); + bindAdrenoStats(cmd); + bindCpuTemps(cmd, root); if (extraBinds != null) { for (String spec : extraBinds) bind(cmd, spec); } @@ -244,6 +246,72 @@ public final class LinuxRuntime { bind(cmd, KGSL_DEVICE + ":/dev/dri/" + node); } + /** + * Valve's mangoapp (Deck mode's performance overlay) reads an Adreno GPU's load, clock and + * temperatures from where they are on Valve's own hardware; every Adreno under Android keeps + * them in KGSL's sysfs, readable by the app. Only the GPU temperature has no KGSL file on older + * kernels, and thermal zones are numbered per device, so a zone is found by its sensor's name, + * which differs between Snapdragon generations. A value that cannot be found is left alone. + */ + private static void bindAdrenoStats(List cmd) { + String kgsl = "/sys/class/kgsl/kgsl-3d0/"; + String gpuTemp = new File(kgsl + "temp").canRead() ? kgsl + "temp" + : thermalZone("gpuss-0", "gpuss-0-usr", "gpu0-usr", "gpu-usr", "gpu"); + String[][] stats = { + {kgsl + "gpu_busy_percentage", "/sys/kernel/debug/dri/0/perf_now"}, + {kgsl + "devfreq/cur_freq", "/sys/devices/platform/soc@0/3d00000.gpu/devfreq/3d00000.gpu/cur_freq"}, + {gpuTemp, "/sys/class/thermal/thermal_zone28/temp"}, + {gpuTemp, "/sys/class/thermal/thermal_zone26/temp"}, + {thermalZone("ddr", "ddr-usr", "ddr-thermal"), "/sys/class/thermal/thermal_zone22/temp"}, + }; + for (String[] stat : stats) { + if (stat[0] != null && new File(stat[0]).canRead()) bind(cmd, stat[0] + ":" + stat[1]); + } + } + + /** + * mangoapp's CPU temperature is the mean of the thermal zones named cpuN-thermal or + * cpuN-top-thermal, as mainline kernels name them; Android kernels name the same sensors + * cpuss-0, cpu-1-0 and the like, so those zones are shown under the mainline name. + */ + private static void bindCpuTemps(List cmd, File root) { + File name = new File(root, "etc/bannerlator/cpu-thermal-type"); + try { + if (!name.isFile()) Files.write(name.toPath(), "cpu0-thermal\n".getBytes(StandardCharsets.US_ASCII)); + } catch (IOException e) { + return; + } + for (java.util.Map.Entry zone : thermalZones().entrySet()) { + String type = zone.getKey(); + if (type.startsWith("cpu") && !type.matches("cpu\\d-(top-)?thermal")) { + bind(cmd, name.getPath() + ":" + new File(zone.getValue(), "type").getPath()); + } + } + } + + /** The temp file of the first thermal zone whose sensor has one of these names, in that order. */ + private static String thermalZone(String... types) { + java.util.Map zones = thermalZones(); + for (String type : types) { + if (zones.containsKey(type)) return new File(zones.get(type), "temp").getPath(); + } + return null; + } + + /** The device's thermal zones by their sensor's name. */ + private static java.util.Map thermalZones() { + java.util.Map byType = new java.util.HashMap<>(); + File[] zones = new File("/sys/class/thermal").listFiles((dir, n) -> n.startsWith("thermal_zone")); + if (zones == null) return byType; + for (File zone : zones) { + try (java.io.BufferedReader r = new java.io.BufferedReader(new java.io.FileReader(new File(zone, "type")))) { + byType.putIfAbsent(String.valueOf(r.readLine()).trim(), zone); + } catch (IOException ignored) { + } + } + return byType; + } + private static void bind(List cmd, String spec) { cmd.add("-b"); cmd.add(spec);