Skip to content

Charging: Distinguish a configured policy from one that is in effect - #73

Merged
d4rken merged 6 commits into
mainfrom
worktree-xiaomi-conditional-enforcement
Aug 17, 2026
Merged

Charging: Distinguish a configured policy from one that is in effect#73
d4rken merged 6 commits into
mainfrom
worktree-xiaomi-conditional-enforcement

Conversation

@d4rken

@d4rken d4rken commented Aug 16, 2026

Copy link
Copy Markdown
Member

What changed

Amply no longer shows a confirmation checkmark for a charge policy it cannot tell is actually working.

Some OEM charging modes only act when the system decides to. Xiaomi's "Intelligent charging" is one: HyperOS learns when you habitually charge overnight and holds at 80% only inside that window. Until now, Amply read the setting back, saw the mode was selected, and presented that as confirmed protection. A Xiaomi 13T with Intelligent charging switched on charged from 59% to 100% untouched while the app showed a green check.

The dashboard now separates "this mode is set" from "this is what the charger is doing". For a mode whose timing the system controls, the card names the mode and says the system chooses when it applies, without the confirmation mark. Fixed limits and unrestricted charging are unaffected, and a mode the charging hardware actively reports as running still shows as confirmed.

The description of adaptive charging now also says it may not hold at other times, which is the part that would have explained a daytime charge to 100% rather than looking like a bug.

Technical Context

  • Root cause: ChargeObservation.Verified conflated "configured value read back" with "hardware is acting on it", and the hero card's icon keyed off a bare is Verified check. Evidence: on the 13T, getNightChargingState returned 0 on all 140 evaluations across a full 59→100% charge with the key verified at 1. The write path is sound; external shell-UID writes drive the HyperOS daemon identically to native UI taps.
  • enforcementIsConditional sits on ChargePolicy, not on ChargingAdapter. Every adapter mapping a mode to Adaptive maps a learned/OS-scheduled one (Pixel adaptive charging, Xiaomi Intelligent charging, ColorOS Smart charging), and ChargeObservation does not carry adapter identity, so identical observations would otherwise mean different things.
  • provesPolicyInEffect() is about knowledge, not safety: Unrestricted returns true because it is in effect as verifiably as a cap. Naming it around "protection" would have been wrong for exactly that case.
  • Presentation only, and that is load-bearing. Two independent sites decide whether a write landed (applyLocked's settled and computeRefreshPending's sync-readback arm). Both must keep clearing on any matching readback; adopting the new predicate in either would spin the settling spinner for the full window on every Xiaomi adaptive apply. Recovery, sessions, NativeChangeGuard, and the reconnect gesture are untouched for equivalent reasons. Worth reviewing that the diff contains no such file.
  • Accepted trade: Pixel under-reports when the HAL reports an engaged adaptive profile, because refreshLocked prefers a settings Verified and discards the state-5 decode. Fixing it means plumbing live hardware state onto ChargingState, which would flip the card between "active" and "configured" repeatedly through one night. Under-claiming matches how the rest of the app fails closed.
  • The HyperOS 2 and 3 adapters no longer share a readiness string; HyperOS 3 keeps the stronger hardware wording its qualification (issue [Device support] Xiaomi 24117RN76G — settings discovery #48) supports.

ChargeObservation.Verified conflated two claims: that a configured value
was read back, and that the charger is acting on it. The dashboard granted
its confirmed checkmark for any Verified, so an OEM mode that the ROM has
not engaged looked identical to an enforced cap.

Physically confirmed on a Xiaomi 13T (aristotle, HyperOS 2, 2026-08-16):
with Adaptive written via the shell UID and read back as Verified(Adaptive,
SHIZUKU), the phone charged 59% to 100% with no hold at any level. HyperOS
engages Intelligent charging only inside a learned overnight window, its
BaseChargeProtect_Night daemon keeping average/stddev/count preferences of
habitual charging; getNightChargingState returned 0 on all 140 evaluations
during the run. The write path is not at fault, external writes drive the
daemon identically to native UI taps.

ChargePolicy.enforcementIsConditional marks Adaptive as a policy whose
timing the OEM decides. ChargeObservation.provesPolicyInEffect() combines
it with the backend: hardware evidence settles any policy, a readback
settles only unconditional ones. It is deliberately about knowledge rather
than safety, so Unrestricted qualifies too, it is in effect exactly as
verifiably as a cap.

Presentation only. The two sites that decide whether a write landed, the
repository's `settled` computation and computeRefreshPending's sync arm,
keep clearing on any matching readback; adopting the predicate there would
spin every Xiaomi adaptive apply for the full settling window, on that
adapter's own protective default. Recovery, sessions, the native-change
guard and the reconnect gesture are untouched for the same reason.

Pixel now under-reports when the HAL reports an engaged adaptive profile,
because refreshLocked prefers a settings Verified over the hardware decode
and discards state 5. Under-claiming is the safe direction and matches how
the rest of the app fails closed; plumbing live hardware state through
would flip the card between states through a single night.

The HyperOS 2 and 3 adapters no longer share a readiness string, so
HyperOS 3 keeps the stronger hardware claim its qualification supports.
@github-actions github-actions Bot added the enhancement New feature or request label Aug 16, 2026
@d4rken d4rken added bug Something isn't working ROM: HyperOS Xiaomi / Redmi / POCO ROM: Pixel Google Pixel and removed enhancement New feature or request labels Aug 16, 2026
@d4rken
d4rken marked this pull request as ready for review August 16, 2026 21:44
… fix

Closes the ledger's open adapter question: the HyperOS 2 protective default
stays Adaptive because the ROM offers nothing unconditional, and the honesty
lives in presentation instead. Verified on aristotle with a foss debug build
over direct WSS, including that neither policy shows a settling spinner,
which is the visible symptom of the predicate leaking into pending logic.
@github-actions github-actions Bot added enhancement New feature or request and removed bug Something isn't working labels Aug 16, 2026
d4rken added 4 commits August 16, 2026 23:49
The charge-rules feature added a required callback to DashboardScreen after
this branch was cut. No textual conflict, but the merged tree stopped
compiling because the new preview passes the callback list explicitly.
The callback arrived on main and was reverted twenty minutes later, so the
preview needed the parameter and then needed it gone. Nothing about this
branch changed either time.
@d4rken
d4rken merged commit 3d31322 into main Aug 17, 2026
12 checks passed
@d4rken
d4rken deleted the worktree-xiaomi-conditional-enforcement branch August 17, 2026 09:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request ROM: HyperOS Xiaomi / Redmi / POCO ROM: Pixel Google Pixel

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant