mirror of
https://github.com/fastfetch-cli/fastfetch.git
synced 2026-10-28 07:44:06 +01:00
Battery (Android): removes dumpsys path
This commit is contained in:
@@ -511,7 +511,7 @@ static void fillTemperature(FFBatteryOptions* options, FFlist* results) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char* parseBinder(FFBatteryOptions* options, FFlist* results) {
|
const char* ffDetectBattery(FFBatteryOptions* options, FFlist* results) {
|
||||||
// Resolved before the binder is opened, so that a jar which does not declare the method says so
|
// Resolved before the binder is opened, so that a jar which does not declare the method says so
|
||||||
// rather than the module failing later with a message about the transport. Both codes are asked
|
// rather than the module failing later with a message about the transport. Both codes are asked
|
||||||
// for in one call, which is cheaper than two: the reader walks the jar's dex entries in order and
|
// for in one call, which is cheaper than two: the reader walks the jar's dex entries in order and
|
||||||
@@ -626,193 +626,3 @@ static const char* parseBinder(FFBatteryOptions* options, FFlist* results) {
|
|||||||
battery->capacity, status, (unsigned) battery->status, battery->timeRemaining);
|
battery->capacity, status, (unsigned) battery->status, battery->timeRemaining);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// `dumpsys battery` prints what `BatteryService.dumpInternal()` prints: the whole of the health HAL's
|
|
||||||
// `HealthInfo`, plus the timestamps the service keeps for its own broadcast rate limiter. It is a flat
|
|
||||||
// `key: value` list read by name, which is what makes it the sturdier of the two routes -- a vendor
|
|
||||||
// that prints extra keys (some vendors do: `engine`, `soc decimal`, `adapter power`, `board temp status`,
|
|
||||||
// `low bat status`, `reverse wl chg status`, `reverse wl chg exception`, `chg shut vbat`,
|
|
||||||
// `last mode flag` and `last mode keep time`, in the middle of the list) only adds keys nobody asks
|
|
||||||
// for, and a release that appends a field costs nothing. The binder reply is positional and breaks
|
|
||||||
// silently instead.
|
|
||||||
//
|
|
||||||
// What each key carries, per `android.hardware.health.HealthInfo`:
|
|
||||||
//
|
|
||||||
// AC / USB / Wireless / Dock powered -- `charger*Online`, the `plugged` bits of the
|
|
||||||
// ACTION_BATTERY_CHANGED broadcast. Only the first three have a `FF_BATTERY_STATUS_*`
|
|
||||||
// counterpart; `Dock powered` (`BATTERY_PLUGGED_DOCK`) has none and is not read.
|
|
||||||
// Max charging current / Max charging voltage -- `maxChargingCurrentMicroamps` (µA) and
|
|
||||||
// `maxChargingVoltageMicrovolts` (µV): what the charger offers, not what the battery is drawing.
|
|
||||||
// Charge counter -- `batteryChargeCounterUah` (µAh), what is left in the pack. The unit is not
|
|
||||||
// honoured everywhere: MIUI reports the same number as its own dump and three orders of
|
|
||||||
// magnitude below what the registrar answers for the same battery.
|
|
||||||
// status -- `batteryStatus`, i.e. `BatteryStatus`: 1 UNKNOWN, 2 CHARGING, 3 DISCHARGING,
|
|
||||||
// 4 NOT_CHARGING (a charger is attached and the battery is deliberately not taking current),
|
|
||||||
// 5 FULL.
|
|
||||||
// health -- `batteryHealth`, i.e. `BatteryHealth`: 1 UNKNOWN, 2 GOOD, 3 OVERHEAT, 4 DEAD,
|
|
||||||
// 5 OVER_VOLTAGE, 6 UNSPECIFIED_FAILURE, 7 COLD, and, in newer releases, 8 FAIR, 11 NOT_AVAILABLE
|
|
||||||
// and 12 INCONSISTENT.
|
|
||||||
// present -- `batteryPresent`, whether the pack is in the device at all.
|
|
||||||
// level / scale -- `batteryLevel`, the remaining capacity in percent, and `BATTERY_SCALE`, the value
|
|
||||||
// 100% is expressed in. The scale has been 100 on every release, but it is printed rather than
|
|
||||||
// assumed, so it is read.
|
|
||||||
// voltage -- `batteryVoltageMillivolts`. The unit in the name is the recent one: the HAL 1.0 field
|
|
||||||
// was `batteryVoltage` in microvolts, and the AIDL notes that implementations had always filled it
|
|
||||||
// in millivolts, so the rename only wrote down the de-facto unit.
|
|
||||||
// temperature -- `batteryTemperatureTenthsCelsius`, tenths of a degree Celsius.
|
|
||||||
// technology -- `batteryTechnology`, e.g. "Li-poly". The registrar answers no such property, so
|
|
||||||
// together with the temperature this is what the route is here for.
|
|
||||||
// Charging state -- `chargingState`, i.e. `BatteryChargingState`: 0 INVALID, 1 NORMAL, 2 TOO_COLD,
|
|
||||||
// 3 TOO_HOT, 4 LONG_LIFE, 5 ADAPTIVE.
|
|
||||||
// Charging policy -- `chargingPolicy`, i.e. `BatteryChargingPolicy`: 0 INVALID, 1 DEFAULT,
|
|
||||||
// 2 LONG_LIFE, 3 ADAPTIVE. 0 in either means the HAL did not report it.
|
|
||||||
// Capacity level -- `batteryCapacityLevel`, i.e. `BatteryCapacityLevel`: -1 UNSUPPORTED,
|
|
||||||
// 0 UNKNOWN, 1 CRITICAL, 2 LOW, 3 NORMAL, 4 HIGH, 5 FULL.
|
|
||||||
//
|
|
||||||
// Three keys are not battery readings at all and are skipped: the two `Time when the latest updated
|
|
||||||
// value of the ... was sent via battery changed broadcast` lines and `The last voltage value sent via
|
|
||||||
// the battery changed broadcast` are what `BatteryService` keeps to rate limit the broadcast.
|
|
||||||
//
|
|
||||||
// `FFBatteryResult` has a place for the status enum, the level pair, the temperature and the
|
|
||||||
// technology, and for the three powered booleans and the capacity level in the form of status bits.
|
|
||||||
// `voltage`, `health`, `Charge counter` and the two maximums are read by nothing -- there is no field
|
|
||||||
// to put them in -- and are described above so that the next reader does not have to go back to the
|
|
||||||
// service to find out what they are.
|
|
||||||
static const char* parseDumpsys(FFBatteryOptions* options, FFlist* results) {
|
|
||||||
FF_STRBUF_AUTO_DESTROY buf = ffStrbufCreate();
|
|
||||||
if (ffProcessAppendStdOut(&buf, (char*[]) {
|
|
||||||
"/system/bin/dumpsys",
|
|
||||||
"battery",
|
|
||||||
nullptr,
|
|
||||||
}) != nullptr ||
|
|
||||||
buf.length == 0) {
|
|
||||||
return "Executing `/system/bin/dumpsys battery` failed";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ffStrbufStartsWithS(&buf, "Current Battery Service state:\n")) {
|
|
||||||
// A refusal, which ffDetectBattery has already ruled out by looking at the UID: an app UID gets
|
|
||||||
// `Can't find service: battery` on Android 16, and the permission denial some other
|
|
||||||
// release would print lands here too. Both exit 0, so this is the only place that notices.
|
|
||||||
return "Invalid `/system/bin/dumpsys battery` result";
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* start = buf.chars + strlen("Current Battery Service state:\n");
|
|
||||||
|
|
||||||
FF_STRBUF_AUTO_DESTROY temp = ffStrbufCreate();
|
|
||||||
if (!ffParsePropLines(start, "present: ", &temp) || !ffStrbufEqualS(&temp, "true")) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
ffStrbufClear(&temp);
|
|
||||||
|
|
||||||
FFBatteryResult* battery = FF_LIST_ADD(FFBatteryResult, *results);
|
|
||||||
initResult(battery);
|
|
||||||
|
|
||||||
if (ffParsePropLines(start, "AC powered: ", &temp) && ffStrbufEqualS(&temp, "true")) {
|
|
||||||
battery->status |= FF_BATTERY_STATUS_AC_CONNECTED;
|
|
||||||
}
|
|
||||||
ffStrbufClear(&temp);
|
|
||||||
|
|
||||||
if (ffParsePropLines(start, "USB powered: ", &temp) && ffStrbufEqualS(&temp, "true")) {
|
|
||||||
battery->status |= FF_BATTERY_STATUS_USB_CONNECTED;
|
|
||||||
}
|
|
||||||
ffStrbufClear(&temp);
|
|
||||||
|
|
||||||
if (ffParsePropLines(start, "Wireless powered: ", &temp) && ffStrbufEqualS(&temp, "true")) {
|
|
||||||
battery->status |= FF_BATTERY_STATUS_WIRELESS_CONNECTED;
|
|
||||||
}
|
|
||||||
ffStrbufClear(&temp);
|
|
||||||
|
|
||||||
// NOT_CHARGING and FULL both mean a charger is attached, which the powered bits already say, and
|
|
||||||
// neither is charging or discharging in the sense `FF_BATTERY_STATUS` means. UNKNOWN is mapped
|
|
||||||
// because it is a different statement from the service not answering at all.
|
|
||||||
if (ffParsePropLines(start, "status: ", &temp)) {
|
|
||||||
switch (ffStrbufToUInt(&temp, 0)) {
|
|
||||||
case FF_BATTERY_ANDROID_STATUS_CHARGING:
|
|
||||||
battery->status |= FF_BATTERY_STATUS_CHARGING;
|
|
||||||
break;
|
|
||||||
case FF_BATTERY_ANDROID_STATUS_DISCHARGING:
|
|
||||||
battery->status |= FF_BATTERY_STATUS_DISCHARGING;
|
|
||||||
break;
|
|
||||||
case FF_BATTERY_ANDROID_STATUS_UNKNOWN:
|
|
||||||
battery->status |= FF_BATTERY_STATUS_UNKNOWN;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ffStrbufClear(&temp);
|
|
||||||
|
|
||||||
// CRITICAL is the one capacity level that says something the percentage does not. -1 (UNSUPPORTED)
|
|
||||||
// and 0 (UNKNOWN) are what a HAL that does not implement the property reports, and 2 to 5 only
|
|
||||||
// restate the level.
|
|
||||||
if (ffParsePropLines(start, "Capacity level: ", &temp) &&
|
|
||||||
ffStrbufToSInt(&temp, -1) == FF_BATTERY_ANDROID_CAPACITY_LEVEL_CRITICAL) {
|
|
||||||
battery->status |= FF_BATTERY_STATUS_CRITICAL;
|
|
||||||
}
|
|
||||||
ffStrbufClear(&temp);
|
|
||||||
|
|
||||||
{
|
|
||||||
double level = 0, scale = 0;
|
|
||||||
if (ffParsePropLines(start, "level: ", &temp)) {
|
|
||||||
level = ffStrbufToDouble(&temp, -DBL_MAX);
|
|
||||||
}
|
|
||||||
ffStrbufClear(&temp);
|
|
||||||
|
|
||||||
if (ffParsePropLines(start, "scale: ", &temp)) {
|
|
||||||
scale = ffStrbufToDouble(&temp, -DBL_MAX);
|
|
||||||
}
|
|
||||||
ffStrbufClear(&temp);
|
|
||||||
|
|
||||||
if (level > 0 && scale > 0) {
|
|
||||||
battery->capacity = level * 100 / scale;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options->temp) {
|
|
||||||
if (ffParsePropLines(start, "temperature: ", &temp)) {
|
|
||||||
battery->temperature = ffStrbufToDouble(&temp, FF_BATTERY_TEMP_UNSET);
|
|
||||||
if (battery->temperature != FF_BATTERY_TEMP_UNSET) {
|
|
||||||
battery->temperature /= 10.0; // Android returns temperature in tenths of a degree
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ffStrbufClear(&temp);
|
|
||||||
}
|
|
||||||
|
|
||||||
ffParsePropLines(start, "technology: ", &battery->technology);
|
|
||||||
|
|
||||||
// The trace the binder route has and this one did not, so that a device reporting the wrong thing
|
|
||||||
// says which of the two routes answered. The temperature is left out: it is only read when `temp`
|
|
||||||
// asks for it, and the sentinel would print as a number.
|
|
||||||
FF_DEBUG("Reporting one battery from `/system/bin/dumpsys battery`: capacity %.0f%%, status bits 0x%x, "
|
|
||||||
"technology \"%s\"",
|
|
||||||
battery->capacity, (unsigned) battery->status, battery->technology.chars);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* ffDetectBattery(FFBatteryOptions* options, FFlist* results) {
|
|
||||||
// `dumpsys battery` needs android.permission.DUMP, which only the shell UID and root hold: an app
|
|
||||||
// UID is answered with `Can't find service: battery` on stdout and a zero exit status, so forking
|
|
||||||
// it there costs a child process and cannot succeed. Where it is allowed to run it is still the
|
|
||||||
// richer of the two -- the technology and the critical capacity level are things only it has, now
|
|
||||||
// that the AC / USB / wireless bits come from a property -- so it goes first, and the binder route
|
|
||||||
// covers everything else at ~0.3 ms.
|
|
||||||
//
|
|
||||||
// What the dump does not have is the time estimate, and it returns before the binder route gets
|
|
||||||
// to ask for it: `timeRemaining` is therefore only filled for an app UID, where the binder route
|
|
||||||
// is the only one that runs. That is the same answer 2.69.0 gave (its dump route did not fill it
|
|
||||||
// either), so it is a gap rather than a regression, but it is a gap.
|
|
||||||
//
|
|
||||||
// termux-api is the third way to reach the same fields and is not used. It does answer, but it
|
|
||||||
// execs `am broadcast` and then reads a socket waiting for an app to write back, with no timeout
|
|
||||||
// of its own, and on the device this was measured on it hung on six of six runs, each killed at
|
|
||||||
// 30 seconds having produced nothing.
|
|
||||||
if (ffAndroidIsRootOrShell(instance.state.platform.uid)) {
|
|
||||||
const char* error = parseDumpsys(options, results);
|
|
||||||
if (error == nullptr) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
FF_DEBUG("`/system/bin/dumpsys battery` failed: %s. Using the binder route instead", error);
|
|
||||||
}
|
|
||||||
|
|
||||||
return parseBinder(options, results);
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user