Skip to content

Commit 81c6836

Browse files
authored
Charging: Record the tanzanite session re-verification in the ledger (#71)
The 2026-08-14 beta run on the Redmi Note 14 (tanzanite) failed its temporary-session check: the native-change observer cancelled on a notification carrying no value change, so the device charged to 100% with Battery protection never restored. That was fixed by NativeChangeGuard (#65), which shipped in v0.3.4-beta0. The contributor re-ran the session test on that build (issue #48): the session reached 100% and Battery protection was re-written automatically with the cable still connected. RESTORE_FULL therefore fires without a disconnect, and the guard holds against the exact notification pattern that produced the bug. Ledger row and known-gaps bullet updated; the remaining unrun items on this device are the disconnect-early restore, the Shizuku tier, boot recovery, and an R8 smoke.
1 parent 1a35f5d commit 81c6836

1 file changed

Lines changed: 11 additions & 3 deletions

File tree

  • .claude/skills/device-qualification

.claude/skills/device-qualification/SKILL.md

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ only after adding a row here. Detailed run narratives live in each adapter's lan
5151
| 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 |
5252
| 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 |
5353
| 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-
| 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; see Known gaps). **NOT run**: Shizuku tier, boot recovery, wireless, R8, session re-verify post-fix | 2026-08-14 |
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 |
5555
| GrapheneOS (follow-up) | Same device, **0.3.2-beta0 on-device report via issue #49** | **Package detection VERIFIED from app context** (`is_grapheneos=true` with the FLAG_SYSTEM check); **unprivileged key read DENIED**`has_battery_charge_limit=false` while the very same report showed `battery_charging_status=4` (limit enforcing). Root cause in GrapheneOS source: the key is `@Protected(read = SYSTEM_UI, readWrite = SETTINGS)` (frameworks_base `c30c6393`); SettingsProvider throws SecurityException for all other packages **including WSS holders**, with the shell UID explicitly exempt ("ADB is used for testing", `e87c93a2`) — so the tester's earlier adb runs ARE the Shizuku-path evidence. Factory-absent semantics resolved from source: `BoolSetting(..., default false)` → absent = off | Detection + fail-closed probe verified live; adapter re-gated to Shizuku-only in response | 2026-08-13 |
5656

5757
## Known gaps
@@ -234,8 +234,16 @@ only after adding a row here. Detailed run narratives live in each adapter's lan
234234
re-written. Root-caused from the run's artifacts — the contributor's 22:22 screenshot shows the
235235
dashboard already idle at 96% while their 2s key monitor shows no value change after the 21:59:08
236236
override write. Fixed by `NativeChangeGuard` (readback-verified cancellation, see
237-
`rules/architecture.md`); **session-lifecycle re-verification is the headline ask for the next beta**.
238-
Boot recovery and R8 smoke remain NOT RUN.
237+
`rules/architecture.md`).
238+
- **Session restore RE-VERIFIED PASS (2026-08-16, issue #48, v0.3.4-beta0 — the first release carrying
239+
the #65 fix).** With Battery protection active, the contributor started the temporary full-charge
240+
session; the battery charged past the cap to 100% and Amply re-wrote Battery protection automatically
241+
on reaching full, **with the cable still connected**. So the `RESTORE_FULL` path fires without needing
242+
a disconnect, and `NativeChangeGuard` no longer eats the session on this device's notification
243+
pattern — the guard fix is now confirmed against the exact device that produced the bug. This closes
244+
the only failing item from the 2026-08-14 run; `tanzanite` becomes the second remote-qualified device
245+
with a verified session lifecycle. **Still NOT run** here: the disconnect-early restore path
246+
(`RESTORE_DISCONNECTED`), the Shizuku access tier, boot recovery, and an R8 beta/release smoke.
239247
- **Cap is fixed at 80%** (no percent picker in the Battery protection screen), and mode `2`'s own
240248
description says the device will "charge fully only when scheduled" — HyperOS reserves an OEM-side
241249
scheduled full charge while in Battery protection. No code impact (verification is settings readback;

0 commit comments

Comments
 (0)