You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Charging: Distinguish a configured policy from one that is in effect (#73)
* Charging: Distinguish a configured policy from one that is in effect
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.
* Charging: Record the on-device verification of the conditional-policy 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.
* Fix: Pass onOpenConditions in the conditional-policy preview
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.
* Fix: Drop onOpenConditions again after the charge-rules revert
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.
| Samsung | Galaxy Tab A9+ SM-X210 One UI 8.0; Galaxy S20 FE SM-G781B One UI 4.1 | Full — sync readback + HAL enforcement | Modern multi-mode + legacy toggle, session E2E, native-change cancel, reboot recovery, R8 beta | 2026-07-21 |
51
-
| Xiaomi | Xiaomi 13T `2306EPN60G` HyperOS 2.0 (`ro.mi.os.version.code=2`) |**Partial** — mapping/readback/session verified; the adaptive 80% hold could not be triggered, so daemon-level hardware enforcement is **not yet demonstrated**| Read matrix, both-direction writes, session at 100%, unknown-value refusal, R8 beta | 2026-07-21 |
51
+
| Xiaomi | Xiaomi 13T `2306EPN60G` (`aristotle`) HyperOS 2.0 (`ro.mi.os.version.code=2`); 2026-08-16 re-run on `OS2.0.216.0.VMFEUXM` / Android 15 | **Partial, now characterized** — external shell-UID writes provably drive the daemon identically to native UI taps (`getProtectMode` → `checkUiModeProtect` → `setEnable`, ~80 ms, both directions, no hidden UI-only flag). The adaptive 80% hold is still **unobserved**: charged 59→100% with no plateau, `getNightChargingState` = 0 on all 140 evaluations. Cause identified — the gate is a *learned* charging-routine model (`key_ave_night_charge_start_minutes`, `…_sd`, `key_enter_night_charge_times`), not a clock window (forced clock to 02:30 did not open it). **BLOCKED on an unused test device, not FAILED.** No hardware hold signal (`Charging state`/`policy` = 0/0) | Read matrix, both-direction writes, session at 100%, unknown-value refusal, R8 beta (2026-07-21). Added 2026-08-16: external-vs-UI write-path equivalence, native UI reflects external key, learned-schedule root cause, clock-forcing negative | 2026-07-21 / 2026-08-16 |
52
52
| OnePlus (Oplus) | OnePlus Nord CE4 Lite `CPH2621` ColorOS 15 (`ro.build.version.oplusrom=V15.0.0`) | Full — enforcement directly observable (device holds at 80%); external writes stick | Two mutually-exclusive `system` keys (Charging limit / Smart charging), WSS-only write rejected + Shizuku write succeeds for all three policies, WSS-only UX (controls disabled + Shizuku-required banner) | 2026-07-21 |
53
53
| GrapheneOS | Pixel 9 Pro XL `komodo`, GrapheneOS 2026080501 / Android 17 — **REMOTE qualification via issue #49** (tester-run protocol, not maintainer hardware) |**Enforcement observed**: held at 80% with shield, `dumpsys battery` status=4/Charging state=4/policy=2 (limit on) vs 2/1/1 (off); shell-UID writes move the Settings UI live, **latch at plug-session start** — mid-session writes have no hardware effect until unplug→replug, replug reliably applies the current value | Key isolation (`settings list` diff → single `global battery_charge_limit` 0/1), write→UI both directions, mid-session no-op both directions, replug latch both directions, hardware signal both states. **NOT run**: app-context access tiers (WSS write from Amply, `app.grapheneos.*` package visibility), sessions/boot recovery, wireless, factory-absent key state, secondary user | 2026-08-12 |
54
54
| Xiaomi (HyperOS 3) | Redmi Note 14 `24117RN76G` (`tanzanite`), HyperOS 3.0.302 / Android 16 (`ro.mi.os.version.code=3`) — **REMOTE qualification via issue #48** (contributor-run protocol, not maintainer hardware) | **Both-direction enforcement of EXTERNAL shell-UID writes observed** (the same write path as Amply's Shizuku service): `settings put … 2` below the cap → Battery protection active, Settings UI follows immediately, held at 80% for ~20 min under active use (voltage 4228 mV holding vs 4391 mV charging, charge counter 4283 vs 4341 corroborate; sysfs `current_now` permission-denied, so no current reading); `settings put … 0` mid-hold → charging resumes past 80 immediately. **No hardware hold signal**: `dumpsys battery` reports `status: 2` / `Charging state: 0` / `Charging policy: 0` in both states → read-back-only verification | Key mapping (three modes incl. `2` = Battery protection @80, cap fixed — no percent picker), external write → UI both directions, sustained hold, mid-hold release. Beta run 2026-08-14 added: app-context three-mode control (direct WSS), factory-absent key = Intelligent (confirmed). Session restore FAILED in that run (observer noise cancel — app bug, fixed in #65). Re-verified 2026-08-16 on v0.3.4-beta0: **session restore PASSES** — full-charge session ran to 100% and Battery protection was re-written automatically while still plugged. **NOT run**: Shizuku tier, boot recovery, wireless, R8, unplug-early restore | 2026-08-16 |
@@ -149,8 +149,60 @@ only after adding a row here. Detailed run narratives live in each adapter's lan
149
149
(`ro.build.version.opporom`) is read, so pre-ColorOS-12 builds remain undetectable as Oplus by ROM version. A
150
150
device with a *working* pre-15 ColorOS charge-protection feature would need a new gate signal, not a widened
151
151
version constant — and the usual bar applies: physically observed charging cessation, not a settings mapping.
152
-
-**Xiaomi** — adaptive hardware enforcement of external writes unconfirmed; treat the adapter as provisional until
153
-
the 80% hold is physically observed.
152
+
-**Xiaomi** — **external-write handling RESOLVED (2026-08-16); the adaptive 80% hold remains unobserved and is
153
+
now believed UNQUALIFIABLE on an unused test device.** Split the old "adaptive enforcement unconfirmed" gap into
154
+
its two halves — one is now closed, the other is characterized rather than merely open.
155
+
-**Closed: external shell-UID writes are functionally identical to native UI taps** (Xiaomi 13T `aristotle`,
156
+
HyperOS 2 `OS2.0.216.0.VMFEUXM`, Android 15, maintainer hardware). Both paths produce the same daemon chain
0 commit comments