Skip to content

CURRENT_NOW unit auto-calibration: show the real current on mA-unit devices (#152 v2)#170

Merged
almothafar merged 3 commits into
masterfrom
fix/152-current-unit-calibration-v2
Jul 17, 2026
Merged

CURRENT_NOW unit auto-calibration: show the real current on mA-unit devices (#152 v2)#170
almothafar merged 3 commits into
masterfrom
fix/152-current-unit-calibration-v2

Conversation

@almothafar

Copy link
Copy Markdown
Owner

Stacked on #169 (v1 floors) — review/merge that first; this PR's diff is just the calibration commit.

v1 stops the Kirin garbage from being displayed; this makes the readings correct there instead of hidden.

How it works

New CurrentUnitCalibrator, hooked into the single read point SystemService.getInstantaneousCurrentMicroAmps, so the rate tracker, the Current row, and ChargeSpeed all see the same corrected value:

  • Persistently tracks the maximum |raw| CURRENT_NOW value ever observed (across sessions, charging and discharging alike).
  • A genuine-µA device exceeds 20,000 (20 mA) within moments of awake use — screen-on draw alone is 100,000+. A mA-unit device physically can't reach it (that would be a sustained 20+ A).
  • After 60 readings spaced ≥ 1 min apart (≥ 1 h cumulative observation) with the max still below the floor → conclude mA units → scale readings ×1000.

Safety properties:

  • Observation records the raw value (pre-scaling), so scaled outputs can't feed back into the decision — no oscillation.
  • A later large reading (e.g. firmware fixes the unit) voids the conclusion in the same call, immediately and permanently. That ordering also caps any scaled value at < 20,000,000 µA — no int overflow.
  • Preference churn is bounded: the max plateaus in minutes, counting stops for good at the cap; the steady-state path writes nothing.
  • Sentinels (Integer.MIN_VALUE/MAX_VALUE) and blank 0 pass through untouched.

Side benefit: once calibrated, source A of the %/h rate (current ÷ capacity) works automatically with scaled current on any such device where capacity is known.

Known transient: at the moment the conclusion first flips on, the rate window still holds pre-scaling samples for up to 10 minutes; on Kirin source A is off anyway (capacity 0), so this is harmless and self-heals.

Tests

Pure helpers fully covered (observe fold: max tracking, spacing throttle, count cap, unchanged-instance contract; isMilliAmpUnits boundaries at the floor and the sample cap; scaling incl. sign) plus Robolectric passthrough tests for the entry point. testDebugUnitTest + lintDebug pass.

On-device verification needed before merging (Mate 10 Pro)

  • Confirm raw magnitudes match expectation (~200–2000 discharging, ~1000–2000 charging) — e.g. temporarily log getIntProperty(CURRENT_NOW)
  • After ~1 h of use, the Current row/notification shows realistic values (hundreds of mA, not ±1)
  • Charging tier + wattage look sane while charging; no false slow-charge warning

Closes #152

🤖 Generated with Claude Code

almothafar and others added 3 commits July 17, 2026 03:31
Kirin/HiSilicon reports CURRENT_NOW in mA instead of uA, so a real
~800 mA draw arrives as raw -800 and displayed as a nonsense
"Discharging -1 mA" in the ongoing notification (and intermittently
in the details table). Require the displayed current to round to at
least MIN_DISPLAY_CURRENT_MA (10): the misread values cap out at
~1-3 "mA" and disappear, while on genuine-uA devices a sub-10 mA
draw only happens in deep sleep, where nothing is being read anyway.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The same Kirin mA-unit misread flows into ChargeSpeed.powerMilliwatts
(uA x mV / 1e6): a raw ~1000 while fast charging yields ~3-4 mW, which
passed the > 0 check and classified as a known Trickle tier. That
mislabeled the charge-connected announcement and the notification's
charging segment as "Slow charging" (#125), and made SlowChargeDetector
(#123) fire a false "check the cable and port" warning on every wired
charge below 80%.

A phone that reports CHARGING is never genuinely taking in under
~0.1 W, so add MIN_PLAUSIBLE_MW (100) as the mirror of the existing
MAX_PLAUSIBLE_MW guard. Unknown power makes the tier displays fall
back to the plain label and keeps the slow-charge detector asleep
(its powerKnown gate).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…es (#152 v2)

The floors in v1 stop garbage from being displayed, but on Kirin the
current is simply hidden. Track the maximum |raw| CURRENT_NOW value
ever observed (persisted across sessions): a genuine-uA device exceeds
20,000 (20 mA) within moments of awake use, while a mA-unit device
physically cannot (that would be 20+ A). After enough spaced readings
(60 at >= 1 min apart, >= 1 h cumulative) with the max still below the
floor, conclude mA units and scale readings x1000 at the single read
point (SystemService.getInstantaneousCurrentMicroAmps), so the rate
tracker, the Current row, and ChargeSpeed all see the corrected value.

Observation always records the raw (pre-scaling) value, so the
conclusion cannot oscillate, and a later large reading voids it
immediately and permanently in the same call. Preference writes are
bounded: the max plateaus and counting stops at the cap.

Needs on-device verification on the Mate 10 Pro before merging
(expected raw magnitudes: ~200-2000 discharging, ~1000-2000 charging).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Base automatically changed from fix/152-kirin-current-floors-v1 to master July 17, 2026 12:50
@almothafar
almothafar merged commit a602cd4 into master Jul 17, 2026
1 check passed
@almothafar
almothafar deleted the fix/152-current-unit-calibration-v2 branch July 17, 2026 12:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Kirin (Mate 10 Pro): ongoing notification shows nonsense "Discharging −1 mA" — CURRENT_NOW reported in mA, not µA

1 participant