From 605c66b093e702560f39ac60a123ea09ee35b063 Mon Sep 17 00:00:00 2001 From: kenway214 Date: Fri, 24 Oct 2025 19:54:05 +0530 Subject: [PATCH] GameBar: Add device overlay support and comprehensive documentation - Implement GameBarConfig for centralized hardware path management - Replace hardcoded sysfs paths with configurable resources - Add config.xml with device-specific overlay support - Update CPU/GPU/RAM/Battery info classes to use config - README guide with build/integration guide Signed-off-by: kenway214 --- README.md | 258 ++++++++++++++++++++- init/init.gamebar.rc | 27 ++- res/values/config.xml | 38 +++ src/com/android/gamebar/GameBar.kt | 11 +- src/com/android/gamebar/GameBarConfig.kt | 65 ++++++ src/com/android/gamebar/GameBarCpuInfo.kt | 12 +- src/com/android/gamebar/GameBarFpsMeter.kt | 2 +- src/com/android/gamebar/GameBarGpuInfo.kt | 30 ++- src/com/android/gamebar/GameBarMemInfo.kt | 10 +- 9 files changed, 415 insertions(+), 38 deletions(-) create mode 100644 res/values/config.xml create mode 100644 src/com/android/gamebar/GameBarConfig.kt diff --git a/README.md b/README.md index ae36103..ec04d5e 100644 --- a/README.md +++ b/README.md @@ -1 +1,257 @@ -Initial Release of GameBar \ No newline at end of file +# GameBar - Real-time Performance Overlay for Android + +[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) +[![Platform](https://img.shields.io/badge/Platform-Android-green.svg)](https://www.android.com) +[![API](https://img.shields.io/badge/API-33%2B-brightgreen.svg)](https://android-arsenal.com/api?level=33) + +GameBar is a comprehensive real-time performance monitoring overlay for Android devices. It provides detailed system metrics including FPS, CPU/GPU usage, temperatures, and memory statistics with a customizable floating overlay. + +## Features + +- **Real-time FPS Monitoring** - Track frame rates with multiple measurement methods +- **CPU Metrics** - Usage percentage, per-core frequencies, and temperature +- **GPU Metrics** - Usage, clock speed, and temperature +- **Memory Stats** - RAM usage, speed, and temperature +- **Battery Temperature** - Monitor device thermal status +- **Customizable Overlay** - Adjustable position, size, colors, and transparency +- **Per-App Configuration** - Auto-enable GameBar for specific applications +- **Logging & Analytics** - Record and analyze performance data +- **Gesture Controls** - Double-tap screenshot, long-press actions +- **Device-Specific Overlays** - Easy hardware path configuration per device + +## Screenshots + +*Coming soon* + +## Requirements + +- Android 13 (API 33) or higher +- System-level permissions (privileged app) +- LineageOS or AOSP-based ROM + +## Building + +### Prerequisites + +- AOSP/LineageOS build environment +- Android SDK Platform 33+ +- Soong build system + +### Integration into Device Tree + +1. **Clone the repository** into your ROM source: + ```bash + cd packages/apps + git clone https://github.com/yourusername/GameBar.git + ``` + +2. **Include in device makefile**: + + Add to your `device.mk`: + ```makefile + # GameBar Performance Overlay + $(call inherit-product, packages/apps/GameBar/gamebar.mk) + ``` + +3. **Create device-specific overlay** (IMPORTANT): + + Create the overlay directory structure: + ```bash + mkdir -p device///overlay/packages/apps/GameBar/res/values + ``` + + Create `device///overlay/packages/apps/GameBar/res/values/config.xml`: + + **Example overlay configuration:** [View config.xml example](LINK_HERE) + +4. **Configure hardware paths**: + + Edit your device overlay `config.xml`: + + ```xml + + + + + + /sys/class/thermal/thermal_zone19/temp + + + /sys/class/kgsl/kgsl-3d0/temp + + + /sys/class/thermal/thermal_zone78/temp + + + 1000 + + ``` + +5. **Customize init.rc** (if needed): + + Edit `packages/apps/GameBar/init/init.gamebar.rc` to match your device's hardware paths. + Ensure permissions are set for all sysfs nodes used by GameBar. + +6. **Build**: + ```bash + # Clean build (recommended for first build) + m clean + m GameBar + + # Or build entire ROM + brunch + ``` + +## Finding Device-Specific Paths + +Use these ADB commands to find the correct paths for your device: + +```bash +# Find CPU thermal zones +adb shell "for i in /sys/class/thermal/thermal_zone*/type; do echo \$i: \$(cat \$i); done" | grep -i cpu + +# Find GPU thermal zones +adb shell "for i in /sys/class/thermal/thermal_zone*/type; do echo \$i: \$(cat \$i); done" | grep -i gpu + +# Find RAM/DDR thermal zones +adb shell "for i in /sys/class/thermal/thermal_zone*/type; do echo \$i: \$(cat \$i); done" | grep -i ddr + +# Check GPU paths +adb shell "ls -la /sys/class/kgsl/kgsl-3d0/" + +# Check FPS path +adb shell "cat /sys/class/drm/sde-crtc-0/measured_fps" + +# Check RAM frequency path +adb shell "cat /sys/devices/system/cpu/bus_dcvs/DDR/cur_freq" +``` + +## Configuration + +### Overlay Customization + +GameBar supports runtime customization through Settings: + +- **Display Options**: Toggle individual metrics (FPS, CPU, GPU, RAM, temperatures) +- **Visual Style**: Adjust text size, colors, background transparency, corner radius +- **Position**: Choose from 9 predefined positions or enable draggable mode +- **Update Interval**: Set refresh rate (500ms - 5000ms) +- **FPS Method**: Choose between new (SurfaceFlinger) or legacy (sysfs) measurement + +### Per-App Auto-Enable + +Configure GameBar to automatically activate for specific apps: + +1. Open GameBar Settings +2. Navigate to "Per-App GameBar" → "Configure Apps" +3. Select apps from the list +4. GameBar will auto-enable when those apps are in foreground + +## Usage + +### Quick Settings Tile + +1. Add GameBar tile to Quick Settings +2. Tap to toggle overlay on/off +3. Long-press tile to open settings + +### Gesture Controls + +- **Double-tap overlay**: Capture screenshot +- **Single-tap**: Toggle visibility +- **Long-press**: Configurable action (hide, screenshot, or settings) +- **Drag**: Move overlay (when draggable mode enabled) + +### Logging & Analytics + +GameBar includes comprehensive logging features: + +- **Global Logging**: Record all system metrics continuously +- **Per-App Logging**: Separate logs for each configured app +- **Analytics**: View FPS distribution, frame time graphs, temperature charts +- **Export**: Share logs as CSV files + +## SELinux Policy + +GameBar includes SELinux policies for: +- Access to sysfs nodes (thermal, kgsl, drm) +- Overlay window permissions +- System service interactions +- File provider for log sharing + +Policies are automatically included via `sepolicy/SEPolicy.mk`. + +## Permissions + +Required permissions (granted automatically as system app): +- `SYSTEM_ALERT_WINDOW` - Overlay display +- `PACKAGE_USAGE_STATS` - Foreground app detection +- `WRITE_EXTERNAL_STORAGE` - Log file storage +- `ACCESS_SURFACE_FLINGER` - FPS measurement +- `WRITE_SECURE_SETTINGS` - Configuration persistence + +## Troubleshooting + +### Overlay not showing +- Check overlay permission in Settings → Apps → GameBar +- Verify SELinux is not blocking (check `adb logcat | grep avc`) +- Ensure init.rc permissions are applied (`adb shell ls -l /sys/class/...`) + +### Metrics showing "N/A" +- Verify sysfs paths in overlay config.xml match your device +- Check file permissions: `adb shell cat /sys/class/thermal/thermal_zone*/temp` +- Review logcat for file access errors + +### Temperature values incorrect +- Adjust divider values in config.xml +- Most devices use 1000 (millidegrees) or 10 (decidegrees) +- Test: `adb shell cat ` and divide manually + +### Build errors +- Ensure `org.lineageos.settings.resources` is available in your ROM +- Check SettingsLib is included in build +- Verify all resource files are present in res/ directory + +## Contributing + +Contributions are welcome! Please: + +1. Fork the repository +2. Create a feature branch +3. Make your changes +4. Test on your device +5. Submit a pull request + +## License + +``` +Copyright (C) 2025 kenway214 + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +``` + +## Credits + +- Original concept inspired by various gaming overlays +- Built for LineageOS and AOSP-based ROMs +- Community contributions welcome + +## Support + +- **Issues**: [GitHub Issues](https://github.com/kenway214/packages_apps_GameBar/issues) +- **XDA Thread**: *Coming soon* +- **Telegram**: [Pandemonium](https://t.me/pandemonium_haydn) + +--- + +**Note**: This is a system application that requires privileged access. It must be built as part of your ROM and cannot be installed as a regular APK. \ No newline at end of file diff --git a/init/init.gamebar.rc b/init/init.gamebar.rc index ae8e699..92ebbcb 100644 --- a/init/init.gamebar.rc +++ b/init/init.gamebar.rc @@ -1,6 +1,31 @@ +# GameBar init script +# NOTE: Customize these paths for your device's hardware + on boot + # FPS measurement path (adjust paths) chown system graphics /sys/class/drm/sde-crtc-0/measured_fps chmod 0660 /sys/class/drm/sde-crtc-0/measured_fps + # Battery temperature path (adjust paths) chown system system /sys/class/power_supply/battery/temp - chmod 0660 /sys/class/power_supply/battery/temp \ No newline at end of file + chmod 0660 /sys/class/power_supply/battery/temp + + # CPU temperature path (adjust thermal zone number) + chown system system /sys/class/thermal/thermal_zone48/temp + chmod 0660 /sys/class/thermal/thermal_zone48/temp + + # GPU paths (adjust paths) + chown system system /sys/class/kgsl/kgsl-3d0/gpu_busy_percentage + chmod 0660 /sys/class/kgsl/kgsl-3d0/gpu_busy_percentage + chown system system /sys/class/kgsl/kgsl-3d0/gpuclk + chmod 0660 /sys/class/kgsl/kgsl-3d0/gpuclk + chown system system /sys/class/kgsl/kgsl-3d0/temp + chmod 0660 /sys/class/kgsl/kgsl-3d0/temp + + # RAM frequency path (adjust paths) + chown system system /sys/devices/system/cpu/bus_dcvs/DDR/cur_freq + chmod 0660 /sys/devices/system/cpu/bus_dcvs/DDR/cur_freq + + # RAM temperature path (adjust thermal zone number) + chown system system /sys/class/thermal/thermal_zone27/temp + chmod 0660 /sys/class/thermal/thermal_zone27/temp \ No newline at end of file diff --git a/res/values/config.xml b/res/values/config.xml new file mode 100644 index 0000000..283d604 --- /dev/null +++ b/res/values/config.xml @@ -0,0 +1,38 @@ + + + + + + + + /sys/class/drm/sde-crtc-0/measured_fps + + + /sys/class/power_supply/battery/temp + 10 + + + /sys/devices/system/cpu + /sys/class/thermal/thermal_zone48/temp + 1000 + + + /sys/class/kgsl/kgsl-3d0/gpu_busy_percentage + /sys/class/kgsl/kgsl-3d0/gpuclk + /sys/class/kgsl/kgsl-3d0/temp + 1000 + 1000000 + + + /sys/devices/system/cpu/bus_dcvs/DDR/cur_freq + /sys/class/thermal/thermal_zone27/temp + 1000 + + + /proc/stat + /proc/meminfo + + \ No newline at end of file diff --git a/src/com/android/gamebar/GameBar.kt b/src/com/android/gamebar/GameBar.kt index 679a3c1..ab7ad51 100644 --- a/src/com/android/gamebar/GameBar.kt +++ b/src/com/android/gamebar/GameBar.kt @@ -63,8 +63,6 @@ class GameBar private constructor(context: Context) { return sInstance?.isShowing == true } - private const val FPS_PATH = "/sys/class/drm/sde-crtc-0/measured_fps" - private const val BATTERY_TEMP_PATH = "/sys/class/power_supply/battery/temp" private const val PREF_KEY_X = "game_bar_x" private const val PREF_KEY_Y = "game_bar_y" private const val TOUCH_SLOP = 30f @@ -74,6 +72,11 @@ class GameBar private constructor(context: Context) { private val windowManager: WindowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager private val handler: Handler = Handler(Looper.getMainLooper()) + init { + // Initialize config with context + GameBarConfig.init(context) + } + private var overlayView: View? = null private var rootLayout: LinearLayout? = null private var layoutParams: WindowManager.LayoutParams? = null @@ -459,11 +462,11 @@ class GameBar private constructor(context: Context) { // 2) Battery temp - Always collect for logging var batteryTempStr = "N/A" - val tmp = readLine(BATTERY_TEMP_PATH) + val tmp = readLine(GameBarConfig.batteryTempPath) if (!tmp.isNullOrEmpty()) { try { val raw = tmp.trim().toInt() - val celsius = raw / 10f + val celsius = raw / GameBarConfig.batteryTempDivider.toFloat() batteryTempStr = String.format(Locale.getDefault(), "%.1f", celsius) } catch (ignored: NumberFormatException) {} } diff --git a/src/com/android/gamebar/GameBarConfig.kt b/src/com/android/gamebar/GameBarConfig.kt new file mode 100644 index 0000000..66236f8 --- /dev/null +++ b/src/com/android/gamebar/GameBarConfig.kt @@ -0,0 +1,65 @@ +/* + * SPDX-FileCopyrightText: 2025 kenway214 + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.android.gamebar + +import android.content.Context + +/** + * Centralized configuration for GameBar hardware paths and conversion factors. + * All values are loaded from resources to support device-specific overlays. + */ +object GameBarConfig { + + private lateinit var context: Context + + fun init(ctx: Context) { + context = ctx.applicationContext + } + + // FPS paths + val fpsSysfsPath: String + get() = context.getString(R.string.config_fps_sysfs_path) + + // Battery configuration + val batteryTempPath: String + get() = context.getString(R.string.config_battery_temp_path) + val batteryTempDivider: Int + get() = context.resources.getInteger(R.integer.config_battery_temp_divider) + + // 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) + val cpuTempDivider: Int + get() = context.resources.getInteger(R.integer.config_cpu_temp_divider) + + // GPU configuration + val gpuUsagePath: String + get() = context.getString(R.string.config_gpu_usage_path) + val gpuClockPath: String + get() = context.getString(R.string.config_gpu_clock_path) + val gpuTempPath: String + get() = context.getString(R.string.config_gpu_temp_path) + val gpuTempDivider: Int + get() = context.resources.getInteger(R.integer.config_gpu_temp_divider) + val gpuClockDivider: Int + get() = context.resources.getInteger(R.integer.config_gpu_clock_divider) + + // RAM configuration + val ramFreqPath: String + get() = context.getString(R.string.config_ram_freq_path) + val ramTempPath: String + get() = context.getString(R.string.config_ram_temp_path) + val ramTempDivider: Int + get() = context.resources.getInteger(R.integer.config_ram_temp_divider) + + // Proc filesystem paths + val procStatPath: String + get() = context.getString(R.string.config_proc_stat_path) + val procMeminfoPath: String + get() = context.getString(R.string.config_proc_meminfo_path) +} diff --git a/src/com/android/gamebar/GameBarCpuInfo.kt b/src/com/android/gamebar/GameBarCpuInfo.kt index 1f3929d..8be42a2 100644 --- a/src/com/android/gamebar/GameBarCpuInfo.kt +++ b/src/com/android/gamebar/GameBarCpuInfo.kt @@ -17,10 +17,8 @@ object GameBarCpuInfo { private var prevIdle = -1L private var prevTotal = -1L - private const val CPU_TEMP_PATH = "/sys/class/thermal/thermal_zone48/temp" - fun getCpuUsage(): String { - val line = readLine("/proc/stat") + val line = readLine(GameBarConfig.procStatPath) if (line == null || !line.startsWith("cpu ")) return "N/A" val parts = line.split("\\s+".toRegex()) if (parts.size < 8) return "N/A" @@ -56,8 +54,7 @@ object GameBarCpuInfo { fun getCpuFrequencies(): List { val result = mutableListOf() - val cpuDirPath = "/sys/devices/system/cpu/" - val cpuDir = File(cpuDirPath) + val cpuDir = File(GameBarConfig.cpuBasePath) val files = cpuDir.listFiles { _, name -> name.matches(Regex("cpu\\d+")) } if (files.isNullOrEmpty()) { return result @@ -85,12 +82,11 @@ object GameBarCpuInfo { } fun getCpuTemp(): String { - val line = readLine(CPU_TEMP_PATH) ?: return "N/A" + val line = readLine(GameBarConfig.cpuTempPath) ?: return "N/A" val cleanLine = line.trim() return try { val raw = cleanLine.toFloat() - // Device reports in millidegrees (e.g., 33849 = 33.849°C) - val celsius = raw / 1000f + val celsius = raw / GameBarConfig.cpuTempDivider.toFloat() // Sanity check: CPU temp should be between 0 and 150°C if (celsius > 0f && celsius < 150f) { String.format(Locale.getDefault(), "%.1f", celsius) diff --git a/src/com/android/gamebar/GameBarFpsMeter.kt b/src/com/android/gamebar/GameBarFpsMeter.kt index 05037e7..703f046 100644 --- a/src/com/android/gamebar/GameBarFpsMeter.kt +++ b/src/com/android/gamebar/GameBarFpsMeter.kt @@ -114,7 +114,7 @@ class GameBarFpsMeter private constructor(context: Context) { private fun readLegacyFps(): Float { try { - BufferedReader(FileReader("/sys/class/drm/sde-crtc-0/measured_fps")).use { br -> + BufferedReader(FileReader(GameBarConfig.fpsSysfsPath)).use { br -> val line = br.readLine() if (line != null && line.startsWith("fps:")) { val parts = line.split("\\s+".toRegex()) diff --git a/src/com/android/gamebar/GameBarGpuInfo.kt b/src/com/android/gamebar/GameBarGpuInfo.kt index d4b32a9..d9bf7c2 100644 --- a/src/com/android/gamebar/GameBarGpuInfo.kt +++ b/src/com/android/gamebar/GameBarGpuInfo.kt @@ -12,12 +12,8 @@ import java.io.IOException object GameBarGpuInfo { - private const val GPU_USAGE_PATH = "/sys/class/kgsl/kgsl-3d0/gpu_busy_percentage" - private const val GPU_CLOCK_PATH = "/sys/class/kgsl/kgsl-3d0/gpuclk" - private const val GPU_TEMP_PATH = "/sys/class/kgsl/kgsl-3d0/temp" - fun getGpuUsage(): String { - val line = readLine(GPU_USAGE_PATH) ?: return "N/A" + val line = readLine(GameBarConfig.gpuUsagePath) ?: return "N/A" val cleanLine = line.replace("%", "").trim() return try { val value = cleanLine.toInt() @@ -28,26 +24,26 @@ object GameBarGpuInfo { } fun getGpuClock(): String { - val line = readLine(GPU_CLOCK_PATH) ?: return "N/A" + val line = readLine(GameBarConfig.gpuClockPath) ?: return "N/A" val cleanLine = line.trim() - return try { - val hz = cleanLine.toLong() - val mhz = hz / 1_000_000 - mhz.toString() + try { + val hz = line.trim().toLong() + val mhz = hz / GameBarConfig.gpuClockDivider + return "$mhz".toString() } catch (e: NumberFormatException) { - "N/A" + return "N/A" } } fun getGpuTemp(): String { - val line = readLine(GPU_TEMP_PATH) ?: return "N/A" + val line = readLine(GameBarConfig.gpuTempPath) ?: return "N/A" val cleanLine = line.trim() - return try { - val raw = cleanLine.toFloat() - val celsius = raw / 1000f - String.format("%.1f", celsius) + try { + val raw = line.trim().toInt() + val celsius = raw / GameBarConfig.gpuTempDivider.toFloat() + return String.format(Locale.getDefault(), "%.1f", celsius) } catch (e: NumberFormatException) { - "N/A" + return "N/A" } } diff --git a/src/com/android/gamebar/GameBarMemInfo.kt b/src/com/android/gamebar/GameBarMemInfo.kt index c7de95e..3f7872c 100644 --- a/src/com/android/gamebar/GameBarMemInfo.kt +++ b/src/com/android/gamebar/GameBarMemInfo.kt @@ -17,7 +17,7 @@ object GameBarMemInfo { var memAvailable = 0L try { - BufferedReader(FileReader("/proc/meminfo")).use { br -> + BufferedReader(FileReader(GameBarConfig.procMeminfoPath)).use { br -> var line: String? while (br.readLine().also { line = it } != null) { line?.let { @@ -57,9 +57,8 @@ object GameBarMemInfo { } fun getRamSpeed(): String { - val path = "/sys/devices/system/cpu/bus_dcvs/DDR/cur_freq" try { - BufferedReader(FileReader(path)).use { br -> + BufferedReader(FileReader(GameBarConfig.ramFreqPath)).use { br -> val line = br.readLine() if (!line.isNullOrEmpty()) { try { @@ -81,14 +80,13 @@ object GameBarMemInfo { } fun getRamTemp(): String { - val path = "/sys/class/thermal/thermal_zone27/temp" try { - BufferedReader(FileReader(path)).use { br -> + BufferedReader(FileReader(GameBarConfig.ramTempPath)).use { br -> val line = br.readLine() if (!line.isNullOrEmpty()) { try { val raw = line.trim().toInt() - val celsius = raw / 1000f + val celsius = raw / GameBarConfig.ramTempDivider.toFloat() return String.format("%.1f°C", celsius) } catch (ignored: NumberFormatException) { }