GameBar: Add dynamic CPU temp detection

- Replace static overlays with runtime detection for CPU temp sensors
- Add smart temperature format detection and caching
- Support multiple device manufacturers and kernel variants
  (still need testing on older devices just was tested on 5.4(lahaina),5.10(taro),6.1(Pineapple) platforms

include more sepolicy and clean some for our project

Change-Id: Iaa0714b6df687203644a13da03ad048472c40164
Signed-off-by: klozz <carlosj@klozz.dev>
This commit is contained in:
klozz
2025-10-28 03:49:03 -06:00
committed by kenway214
parent 0c3cb7f448
commit 82cb10c59e
8 changed files with 175 additions and 20 deletions

View File

@@ -6,11 +6,6 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<!-- GameBar Device Paths Configuration -->
<!-- CPU Configuration -->
<string name="config_cpu_base_path" translatable="false">/sys/devices/system/cpu</string>
<string name="config_cpu_temp_path" translatable="false">/sys/class/thermal/thermal_zone48/temp</string>
<integer name="config_cpu_temp_divider">1000</integer>
<!-- GPU Configuration -->
<string name="config_gpu_usage_path" translatable="false">/sys/class/kgsl/kgsl-3d0/gpu_busy_percentage</string>
<string name="config_gpu_clock_path" translatable="false">/sys/class/kgsl/kgsl-3d0/gpuclk</string>

View File

@@ -1 +0,0 @@
settingsdebug.instant.packages u:object_r:settingslib_prop:s0

View File

@@ -1,2 +0,0 @@
# SettingsLib
system_public_prop(settingslib_prop)

View File

@@ -4,5 +4,13 @@ allow gamebar_app vendor_sysfs_kgsl:dir search;
allow gamebar_app vendor_sysfs_kgsl:{ file lnk_file } rw_file_perms;
allow gamebar_app vendor_sysfs_battery_supply:dir search;
allow gamebar_app vendor_sysfs_battery_supply:file r_file_perms;
allow gamebar_app vendor_sysfs_usb_supply:dir search;
allow gamebar_app vendor_sysfs_usb_supply:file r_file_perms;
allow gamebar_app proc_stat:file { read open getattr };
allow gamebar_app vendor_sysfs_kgsl_gpuclk:file { read open getattr };
allow zygote gamebar_app:process dyntransition;
allow gamebar_app self:process dyntransition;
allow gamebar_app hal_graphics_mapper_hwservice:hwservice_manager find;
allow gamebar_app default_prop:file read;

2
sepolicy/vendor/genfs_contexts vendored Normal file
View File

@@ -0,0 +1,2 @@
# for battery nodes on GameBar
genfscon sysfs /class/power_supply/battery/temp u:object_r:vendor_sysfs_battery_supply:s0

View File

@@ -41,13 +41,18 @@ object GameBarConfig {
}
// CPU configuration
val cpuBasePath: String
get() = context.getString(R.string.config_cpu_base_path)
val cpuTempPath: String
get() = context.getString(R.string.config_cpu_temp_path)
// CPU configuration - now dynamic
val cpuBasePath: String?
get() = SysfsDetector.getCpuBasePath()
val cpuTempPath: String?
get() = SysfsDetector.getCpuTempInfo().first
val cpuTempDivider: Int
get() = context.resources.getInteger(R.integer.config_cpu_temp_divider)
get() = SysfsDetector.getCpuTempInfo().second
fun getCpuTempConfig(): Pair<String?, Int> {
return SysfsDetector.getCpuTempInfo()
}
// GPU configuration
val gpuUsagePath: String
@@ -82,7 +87,8 @@ object GameBarConfig {
fun isSystemSupported(): Boolean {
return listOf(
SysfsDetector.getBatteryTempPath(),
SysfsDetector.getFpsPath()
SysfsDetector.getFpsPath(),
SysfsDetector.getCpuTempPath()
).any { it != null }
}

View File

@@ -54,7 +54,12 @@ object GameBarCpuInfo {
fun getCpuFrequencies(): List<String> {
val result = mutableListOf<String>()
val cpuDir = File(GameBarConfig.cpuBasePath)
val basePath = GameBarConfig.cpuBasePath
if (basePath == null) {
return result
}
val cpuDir = File(basePath)
val files = cpuDir.listFiles { _, name -> name.matches(Regex("cpu\\d+")) }
if (files.isNullOrEmpty()) {
return result
@@ -82,11 +87,14 @@ object GameBarCpuInfo {
}
fun getCpuTemp(): String {
val line = readLine(GameBarConfig.cpuTempPath) ?: return "N/A"
val (path, divider) = GameBarConfig.getCpuTempConfig()
if (path == null) return "N/A"
val line = readLine(path) ?: return "N/A"
val cleanLine = line.trim()
return try {
val raw = cleanLine.toFloat()
val celsius = raw / GameBarConfig.cpuTempDivider.toFloat()
val celsius = raw / divider.toFloat()
// Sanity check: CPU temp should be between 0 and 150°C
if (celsius > 0f && celsius < 150f) {
String.format(Locale.getDefault(), "%.1f", celsius)

View File

@@ -4,8 +4,8 @@
* SPDX-License-Identifier: Apache-2.0
*/
package com.android.gamebar.utils
import android.util.Log
package com.android.gamebar.utils
import android.util.Log
import java.io.File
/**
@@ -72,6 +72,121 @@ object SysfsDetector {
return Pair(path, divider)
}
/**
* Dynamically searches for CPU temperature node with type-based prioritization
* For cpuss-0, cpuss-1, etc., selects the first available one
*
* @return The path to CPU temperature node or null if not found
*/
private fun findCpuTempPath(): String? {
// Return cached path if available
detectedPaths["cpu_temp"]?.let { return it }
val thermalBaseDirs = arrayOf(
"/sys/class/thermal",
"/sys/devices/virtual/thermal"
)
// Priority order for CPU sensor types (most specific to least specific)
// add more on newer or older devices
val priorityTypes = arrayOf(
"cpu_therm", // Highest priority - specific CPU thermal
"cpuss", // CPU subsystem
"cpu", // Generic CPU
"cluster0" // CPU clusters
)
// Scan all thermal base directories
for (baseDirPath in thermalBaseDirs) {
val baseDir = File(baseDirPath)
if (!baseDir.exists() || !baseDir.isDirectory()) continue
Log.d(TAG, "Scanning thermal directory: $baseDirPath")
// First pass: search by priority type order
for (priorityType in priorityTypes) {
baseDir.listFiles()?.forEach { zone ->
val typeFile = File(zone, "type")
val tempFile = File(zone, "temp")
if (typeFile.exists() && tempFile.exists() && tempFile.canRead()) {
try {
val type = typeFile.readText().trim().lowercase()
// For cpuss, accept any cpuss-* variant (cpuss-0, cpuss-1, etc.)
val isMatch = when {
priorityType == "cpuss" && type.startsWith("cpuss") -> true
else -> type == priorityType
}
if (isMatch) {
detectedPaths["cpu_temp"] = tempFile.absolutePath
Log.d(TAG, "Found CPU temp (priority): ${tempFile.absolutePath} (type=$type)")
return tempFile.absolutePath
}
} catch (e: Exception) {
// Continue to next file on error
}
}
}
}
// Second pass: search for any thermal zone that might be CPU-related
baseDir.listFiles()?.forEach { zone ->
val typeFile = File(zone, "type")
val tempFile = File(zone, "temp")
if (typeFile.exists() && tempFile.exists() && tempFile.canRead()) {
try {
val type = typeFile.readText().trim().lowercase()
// Look for patterns indicating CPU sensor
if (type.contains("cpu") ||
type.contains("core") ||
type.contains("cluster") ||
type.contains("tsens") ||
type.startsWith("thermal")) {
// Read value to verify it's a reasonable temperature
val tempValue = tempFile.readText().trim().toIntOrNull()
if (tempValue != null && tempValue in 20000..90000) { // 20-90°C in milli
detectedPaths["cpu_temp"] = tempFile.absolutePath
Log.d(TAG, "Found CPU temp (fallback): ${tempFile.absolutePath} (type=$type)")
return tempFile.absolutePath
}
}
} catch (e: Exception) {
// Continue to next file on error
}
}
}
}
Log.w(TAG, "No suitable CPU temperature node found")
detectedPaths["cpu_temp"] = null
return null
}
/**
* Gets CPU temperature path and divider information
*
* @return Pair containing path and divider, or (null, 1000) if not found
*/
fun getCpuTempInfo(): Pair<String?, Int> {
val path = getCpuTempPath()
if (path == null) return Pair(null, 1000)
// Return cached divider if available
if (detectedDividers.containsKey("cpu_temp")) {
return Pair(path, detectedDividers["cpu_temp"]!!)
}
// Auto-detect divider and cache it
val divider = detectTemperatureDivider(path)
detectedDividers["cpu_temp"] = divider
return Pair(path, divider)
}
// Possible paths for each hardware component
private val BATTERY_TEMP_PATHS = arrayOf(
"/sys/class/power_supply/battery/temp",
@@ -118,6 +233,27 @@ object SysfsDetector {
fun getBatteryTempPath(): String? = detectPath("battery_temp", BATTERY_TEMP_PATHS)
/** @return CPU temperature sysfs path or null if not supported */
fun getCpuTempPath(): String? = findCpuTempPath()
/** @return CPU base sysfs path*/
fun getCpuBasePath(): String? {
// Check cache first
detectedPaths["cpu_base"]?.let { return it }
val path = "/sys/devices/system/cpu"
val file = File(path)
if (file.exists() && file.isDirectory()) {
detectedPaths["cpu_base"] = path
Log.d(TAG, "Using CPU base path: $path")
return path
}
Log.w(TAG, "CPU base path not found: $path")
detectedPaths["cpu_base"] = null
return null
}
/**
* Clear cache and re-detect all paths (useful for debugging)
*/
@@ -128,11 +264,14 @@ object SysfsDetector {
/**
* Check if a specific hardware component is supported
* for future usage
*/
fun isComponentSupported(component: String): Boolean {
return when (component) {
"fps" -> getFpsPath() != null
"battery_temp" -> getBatteryTempPath() != null
"cpu_temp" -> getCpuTempPath() != null
"cpu_base" -> getCpuBasePath() != null
else -> false
}
}