mirror of
https://github.com/Droid-Deck/DroidDeck.git
synced 2026-11-12 03:21:08 +00:00
Fix MangoHud double-counting battery power and remaining life (#270)
This commit is contained in:
1 parent
356208ca9e
commit
2ca051fe71
2 files changed
+76
-3
No files matched your search
@@ -14,7 +14,7 @@ import kotlin.math.abs
|
||||
* The client reads `/sys/class/power_supply/BAT<n>/...` - a laptop's or a Deck's naming and files
|
||||
* (`energy_now`, `power_now`, `time_to_empty_now`, a `uevent` of `POWER_SUPPLY_*` lines). Android
|
||||
* names its supply `battery` and lays it out differently, and on many phones an app may not read
|
||||
* it at all, so the client showed no battery. This writes a `BAT0` (and a `BAT1`, the Deck's name)
|
||||
* it at all, so the client showed no battery. This writes one `BAT1` (the Deck's name)
|
||||
* from Android's own battery API - the same on every device - into a directory the session binds
|
||||
* over `/sys/class/power_supply`, refreshed every few seconds while the session runs.
|
||||
*
|
||||
@@ -31,6 +31,8 @@ class BatteryComponent(val dir: File, private val vpowerDir: File) : SessionPart
|
||||
private var thread: Thread? = null
|
||||
|
||||
override fun start() {
|
||||
// Older builds exported the same aggregate battery twice; MangoHud counts both supplies.
|
||||
File(dir, "BAT0").deleteRecursively()
|
||||
running = true
|
||||
write()
|
||||
thread = Thread({
|
||||
@@ -39,7 +41,7 @@ class BatteryComponent(val dir: File, private val vpowerDir: File) : SessionPart
|
||||
if (running) write()
|
||||
}
|
||||
}, "battery-sysfs").apply { isDaemon = true; start() }
|
||||
Log.i(TAG, "battery: BAT0/BAT1 written to ${dir.path} every ${PERIOD_MS / 1000} s")
|
||||
Log.i(TAG, "battery: BAT1 written to ${dir.path} every ${PERIOD_MS / 1000} s")
|
||||
}
|
||||
|
||||
override fun stop() {
|
||||
@@ -87,7 +89,7 @@ class BatteryComponent(val dir: File, private val vpowerDir: File) : SessionPart
|
||||
pct <= 15 -> "Low"
|
||||
else -> "Normal"
|
||||
}
|
||||
for (name in listOf("BAT0", "BAT1")) {
|
||||
for (name in listOf("BAT1")) {
|
||||
val bat = File(dir, name).apply { mkdirs() }
|
||||
val attrs = linkedMapOf(
|
||||
"type" to "Battery",
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
package com.droiddeck.launcher.session
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.BatteryManager
|
||||
import java.io.File
|
||||
import org.junit.After
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.TemporaryFolder
|
||||
import org.junit.runner.RunWith
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.RuntimeEnvironment
|
||||
import org.robolectric.Shadows.shadowOf
|
||||
import org.robolectric.annotation.Config
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
@Config(sdk = [28], manifest = Config.NONE)
|
||||
class BatteryComponentTest {
|
||||
@get:Rule val tmp = TemporaryFolder()
|
||||
private var battery: BatteryComponent? = null
|
||||
|
||||
@After fun stop() { battery?.stop() }
|
||||
|
||||
@Test fun oneAggregateBatteryKeepsOverlayPowerAndLifetimeConsistentWithQam() {
|
||||
val supplies = tmp.newFolder("power_supply")
|
||||
val vpower = tmp.newFolder("vpower")
|
||||
start(supplies, vpower, -2_000_000L)
|
||||
assertTelemetry(supplies, vpower)
|
||||
}
|
||||
|
||||
@Test fun upgradingRemovesTheDuplicateRatherThanLeavingItForOverlayDiscovery() {
|
||||
val supplies = tmp.newFolder("power_supply")
|
||||
val vpower = tmp.newFolder("vpower")
|
||||
val legacy = File(supplies, "BAT0").apply { mkdirs() }
|
||||
File(legacy, "power_now").writeText("8000000\n")
|
||||
File(legacy, "charge_now").writeText("4000000\n")
|
||||
start(supplies, vpower, 2_000_000L)
|
||||
assertFalse(legacy.exists())
|
||||
assertTelemetry(supplies, vpower)
|
||||
}
|
||||
|
||||
private fun start(supplies: File, vpower: File, currentUa: Long) {
|
||||
val context = RuntimeEnvironment.getApplication()
|
||||
context.sendStickyBroadcast(Intent(Intent.ACTION_BATTERY_CHANGED).apply {
|
||||
putExtra(BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_DISCHARGING)
|
||||
putExtra(BatteryManager.EXTRA_LEVEL, 50)
|
||||
putExtra(BatteryManager.EXTRA_SCALE, 100)
|
||||
putExtra(BatteryManager.EXTRA_VOLTAGE, 4000)
|
||||
})
|
||||
shadowOf(context.getSystemService(BatteryManager::class.java)).apply {
|
||||
setLongProperty(BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER, 4_000_000L)
|
||||
setLongProperty(BatteryManager.BATTERY_PROPERTY_CURRENT_NOW, currentUa)
|
||||
setLongProperty(BatteryManager.BATTERY_PROPERTY_CURRENT_AVERAGE, currentUa)
|
||||
}
|
||||
battery = BatteryComponent(supplies, vpower).also { it.attach(context); it.start() }
|
||||
}
|
||||
|
||||
private fun assertTelemetry(supplies: File, vpower: File) {
|
||||
val discovered = supplies.listFiles()!!.filter { it.name.contains("BAT") }
|
||||
assertEquals(listOf("BAT1"), discovered.map { it.name })
|
||||
fun read(name: String) = File(discovered.single(), name).readText().trim().toLong()
|
||||
// One 4 Ah battery at 4 V drawing 2 A: 8 W and two hours, regardless of vendor current sign.
|
||||
assertEquals(8_000_000L, read("power_now"))
|
||||
assertEquals(16_000_000L, read("energy_now"))
|
||||
assertEquals(7200L, read("time_to_empty_now"))
|
||||
assertEquals("7200", File(vpower, "secs_until_shutdown_request").readText().trim())
|
||||
assertEquals("50.00", File(vpower, "battery_percent").readText().trim())
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user