Skip to content

Fix 9 small bugs in the shell scripts#309

Open
seyedehsanhadi wants to merge 46 commits into
VR-25:devfrom
seyedehsanhadi:fix/bugs
Open

Fix 9 small bugs in the shell scripts#309
seyedehsanhadi wants to merge 46 commits into
VR-25:devfrom
seyedehsanhadi:fix/bugs

Conversation

@seyedehsanhadi
Copy link
Copy Markdown

Nine bugs I hit while reading through the scripts. All small, no behavior or safety
changes, bash -n clean.

- reboot_resume was dead: le_resume_cap should be _le_resume_cap (accd.sh)
- acc -i printed no charger power: dtr_conv_factor returns via $factor, but the caller
  read its stdout; also $powernow -> $powerNow (batt-info.sh)
- working-switches.log got the wrong {mcc} tag: $$chargingSwitch -> $chargingSwitch
- the crash-log check missed exit 127 ([127] is a char class, not the number) (accd.sh)
- the voltage ctrl-file gate tested the current array (accd.sh)
- acc -ss:: rewrote the config because the case fell through (missing return)
- config import aborted under set -e when there was nothing to keep (set-prop.sh, added || :)
- flash-zips.sh exited on an unset var instead of $?
- acc -u had a stray local at script scope (acc.sh)

Happy to split these into separate commits if you'd rather review them one by one.

seyedehsanhadi and others added 15 commits May 31, 2026 01:23
fix3: prefer a charging switch whose off-state persists (skip flicker-prone level switches when a clean one exists; lenient fallback so a device with only a flickery switch is still capped).

fix4: never re-enable charging while pausing at/above the limit; select a switch once per accd session instead of re-probing every loop; fail-safe pause/resume capacity comparators (missing value => pause / do not resume).

fix5: on Pixel/Tensor, disable the dynamic google charge_stop_level 'battery/capacity' variant (it ratchets and makes the firmware re-resume at the threshold); the fixed '100 5' variant holds the limit without churn.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
acc -t waits up to _STI=35s per switch; on a long list that overruns AccA's 150s cap before finishing. This polls the charging current ~3x/sec, decides each switch in ~1-4s, tests the whole device switch list, ranks working ones fastest-first, and prints the exact 'acc -s s=... --' command. Standalone; stops accd only for the scan and restarts it (EXIT trap), restores each switch to on after testing.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
google charge_stop_level is a charge LIMIT ("charge to N%, hold"), not an
on/off switch. Driving it 100/5 (fix5/fix6) writes 5 to pause, which makes the
firmware DISCHARGE; accd then resumes at resume_capacity and re-charges,
overshooting the cap in a 70<->limit sawtooth (reported breach 75 -> 77 on a
Pixel 9a, tegu).

- ctrl-files.sh: prefer "charge_stop_level 100 pcap" (pcap = pause_capacity), so
  the firmware holds the battery flat at the cap (tight, no overshoot, true
  idle). Keep "100 5" (discharge) and "100 battery/capacity" as fallbacks.
- misc-functions.sh flip_sw: resolve the "pcap" off-token to pause_capacity;
  fail-safe to a low cap if unset (can only stop, never run on).
- acc-switch-scan.sh: resolve pcap and rank idle/flat-hold above discharging, so
  --apply locks the flat-hold variant, not the sawtoothing discharge one.
- misc-functions.sh disable_charging: on an unconfirmed pause, return 7 and let
  the loop retry (2022/2023 behavior) instead of exec-ing a daemon re-init
  mid-pause (which re-armed charging in its window and thrashed flaky switches).

versionCode kept 202505180 for AccA front-end compatibility.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…loys

AccA's bundled installer (res/raw/install) skips installing when the bundled
versionCode is <= the already-installed one. Keeping 202505180 across fix4..fix6
meant a new bundle would NOT replace an existing 202505180 daemon on update --
the fix never reached devices that already had ACC. Bumping the patch code makes
the gate pass; handler dispatch in AccA is range-based (>=202107280) so this is
compatible. AccA bundledVersion is updated to match.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- _ge_pause_cap / _le_resume_cap: treat a NON-numeric pause/resume capacity as
  "pause now / do not resume" (numeric guard), not just empty. A garbage value
  used to slip past the empty-only check, make the numeric test error out, and
  silently skip the cap (overcharge).
- disable_charging: a switch locked with `--` that STOPS working now runs the
  auto fallback (re-selects a working switch) and warns, instead of failing
  silently. `--` still suppresses routine re-cycling while the switch works, so
  no churn in the normal case.
- versionCode 202505181 -> 202505182.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… watchdog

- accd.sh: extend the numeric/empty fail-safe guard to ALL capacity comparators
  (_lt_pause_cap, _le_pause_cap, _gt_resume_cap, _le_shutdown_cap, _ge_cooldown_cap)
  -- previously only _ge_pause_cap and _le_resume_cap were guarded. A malformed
  (non-numeric) limit can no longer make any check error out and skip a stop.
- ctrl-files.sh: add the `pcap` (hold-at-pause_capacity) off value to
  /proc/driver/charger_limit too, so non-Google charge-limit chipsets also hold
  the cap flat (firmware-native, no overshoot), not just the Google node.
- accd.sh: breach watchdog -- if the battery is at/above the limit and charging
  still has not stopped, post a throttled warning instead of failing silently;
  self-clears once charging stops (and on daemon start).
- versionCode 202505182 -> 202505183.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…obustness

- acca.sh daemon_ctrl: `acca -D restart` launched the daemon with a bare `exec accd`,
  i.e. inside the calling process. When the caller is a one-shot script (the switch
  scanner run from a front-end), the daemon died the instant that script exited,
  leaving charging UNCAPPED. Launch it detached (setsid, with start-stop-daemon -b /
  nohup fallbacks) so it survives.
- accd.sh: add _nap(), an interruptible sleep that wakes the moment $config changes,
  and use it for the three per-iteration loop waits. The daemon already re-reads the
  config each loop, so AccA edits (limits, temps, switch) now apply within ~1s
  instead of after the full loop delay -- live, no restart, no UI freeze.
- acc-switch-scan.sh: re-arm charging before each test (wait for current to resume)
  so every switch incl. the flat-hold `pcap` variant is evaluated instead of skipped
  as "not charging"; and verify the daemon actually came back after a scan, warning
  loudly (charging UNCAPPED) if not.
- versionCode 202505183 -> 202505184.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…on=100 variants

The 75->77 breach was the ON value: charge_stop_level "100 5" / "100 battery/capacity"
recharge with on=100 ("charge to 100%"), so on every resume the firmware sails toward
100 and accd catches it late -> overshoot. Drive the limit node with the TARGET level
on BOTH sides so the recharge stops EXACTLY at pause_capacity:

- ctrl-files.sh google charge_stop_level is now `pcap pcap` (charge to the cap and
  hold -- battery-idle, DEFAULT) and `pcap 5` (discharge to resume_capacity then
  recharge to the cap, repeat -- the cycle when battery-idle is off; recharge still
  tops out exactly at the cap). Removed `100 5` and `100 battery/capacity`.
- misc-functions.sh flip_sw: `pcap` now resolves to pause_capacity on the ON side too
  (it was off-only), so on=pcap recharges to exactly the cap instead of 100.
- versionCode 202505184 -> 202505185.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… = discharge)

The scanner already locks the best switch with `--apply`. Add a method choice:
- `--apply`          -> lock "hold at the limit" (pcap pcap)  [DEFAULT, unchanged].
- `--apply --cycle`  -> lock the "discharge-cycle" (pcap 5): discharge to
                        resume_capacity, then recharge to the cap, repeat.
The method is chosen by the switch LINE (`... pcap pcap` vs `... pcap 5`), not the
device-dependent measured idle/discharging reading, so it is reliable on devices
that slow-drain at the limit. Header/result text reflect the chosen method.
versionCode 202505185 -> 202505186.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Consolidates fix2..fix12 into the first stable release, plus final hardening:
- default-config.txt + write-config.sh: allow_idle_above_pcap now defaults FALSE --
  never sit ABOVE the limit; if the battery is over it, discharge down to it (also
  better for longevity: a lower resting SoC).
- misc-functions.sh flip_sw: "pcap" falls back to a safe low cap (60) when
  pause_capacity is empty OR non-numeric, matching the daemon's fail-safe rule
  (a garbage config can only cap low, never run charging on).
- accd.sh: breach-watchdog notification points at the current "Scan & lock" scripts.
- versionCode 202505186 -> 202505187; label v2025.5.18-stable.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…o command)

So users never have to run `acc -s`. install.sh now does a one-time, marker-guarded
config migration that flips the OLD default allowIdleAbovePcap=true -> false ("never
sit above the limit") on existing configs. Runs once, only touches that single key,
and never clobbers a deliberate later choice. Fresh installs already ship the new
defaults. versionCode 202505187 -> 202505188.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…the migration

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@VR-25
Copy link
Copy Markdown
Owner

VR-25 commented May 31, 2026

Great progress! 👏
I'll be reviewing the work this week.

seyedehsanhadi and others added 14 commits May 31, 2026 23:09
stable.1's allow_idle_above_pcap=false default pushed auto-mode into the discharge
variant (charge_stop_level pcap 5), which drained the battery down to resume_capacity
(~70) instead of holding at the limit. Fix: remove the pcap-5 variant entirely (only
pcap pcap = hold at the limit) and revert allow_idle_above_pcap to its original
default. install.sh migrates existing configs (undo the false default, drop a locked
pcap-5 switch). Charging now stops at your limit and holds there -- the only behavior.
versionCode 202505188 -> 202505189.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The Google charge-stop node LATCHES "stopped" once it reaches the limit. stable.2's
`charge_stop_level pcap pcap` used the limit value (75) for BOTH stop and resume, and
re-writing the limit never re-arms the charger -- so once stopped it stayed stopped,
even after the battery drained below the range (user saw it frozen at 64, neither
charging nor discharging). Fix: switch is now `charge_stop_level 100 pcap` -- ON=100
("charge up") re-arms the charger (what 2022/2023 did), OFF=pcap stops AT the limit.
loopDelay[0]=3s keeps the stop tight (no overshoot). install.sh upgrades a locked
pcap-pcap/pcap-5 switch to 100 pcap automatically. versionCode 202505189 -> 202505190.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- ACC is now independent of the AccA app. Removed the service.d cleanup script that
  deleted ACC (module + daemon) when AccA was uninstalled; install.sh also removes any
  leftover on update. Uninstalling the front-end no longer touches the daemon or the
  persistent config (config.txt). The accaFiles symlink stays so AccA can still find it.
- Default max_temp 50 -> 45 C (heat above ~45 ages the cell faster). default-config +
  write-config fallback; one-time migration updates a config still on the old 50 (a
  deliberate value is left alone).
- versionCode 202505190 -> 202505191.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
stable.4 lowered max_temp 50->45, leaving it equal to cooldown_temp
(temperature=(45 45 40 55)). cooldown_temp starts the cooldown cycle and
max_temp is the hard pause; when equal, the cooldown loop enters and
immediately breaks at max_temp, so it never throttles. Restore the upstream
band temperature=(45 50 40 55).

- write-config: default mt=50 and enforce cooldown_temp < max_temp
  (and >= resume_temp) on every rewrite, so it cannot re-collapse.
- migration: replaced the 50->45 sed in all three installers with a
  .stable-defaults5 repair that turns a stuck (45 45 ...) back into
  (45 50 ...); only the exact collapsed signature is touched.
- versionCode 202505191 -> 202505192; README/module.json regenerated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The stable.5 build ran build.sh on a host with no `markdown` binary, which
(1) truncated README.html to 0 bytes (the `markdown > README.html` redirect) and
(2) leaked the build host's TMPDIR into README.md via print_help
(/ch-switches -> /c/Users/.../Temp/...). Restore both READMEs from the last clean
commit (they already embed temperature=(45 50 40 55) and the correct on-device
paths) and harden build.sh to rewrite README.html only when a markdown converter
actually produces non-empty output.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
New install/state-export.sh publishes $TMPDIR/state.json (tmpfs) every daemon
loop and on demand via `acca --state` / `acc -j`. It is the read-back the control
bus confirms against, the diagnostics feed, and the report source — all one file.

Contracts enforced + tested: atomic (temp+rename, no torn reads); best-effort and
non-blocking (never stalls the safety loop); tmpfs-only (no flash wear); fingerprint
is non-PII (model|soc|hardware|buildId, no serial/IMEI); and rule S1 — a value that
can't be read is JSON null, never 0 (proven: a garbage current read -> null).

Wiring: registered in misc-functions.sh (seen by daemon + CLI); write_state hooked
at the top of the accd loop; `-j|--state` added to the acc.sh dispatcher.

Local only, not pushed. First increment of the A-E rebuild.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
RC1 toward stable.6. Ships the acca --state state export (prior commit) as the
keystone for the AccA control-bus + diagnostics rebuild. Additive only -- no
charging-behavior change. Hard-test gate (30 checks: temperature band, write-config
cooldown<max guard, all three installer migrations, README integrity, state-export
JSON validity + S1 null-on-bad-read, flashable-zip contents, and the parser/fetch/
device fuzz models) ALL PASS.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Found on-device: acc.version/versionCode empty (accVer is only set in acc.sh, not
the daemon/acca paths) -> now read from module.prop directly; status "unknown" on
the front-end path (it never calls read_status) -> now derived from the current sign
(Charging/Discharging/Idle); and a frozen snapshot (print_state cat an existing file)
-> print_state now always refreshes. Additive; no charging-behavior change. State
export functional test (version + derived status + refresh) passes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…larity/measuredClass)

Adds live measured sensing to acca --state, generalized beyond Tensor: plugged (any
*/online), currentUnits (uA/mA auto), polarity (status vs current sign), and
switch.measuredClass (bypass/idle/charging/discharging from plug + unit-aware current).
Additive; no charging-behavior change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…-- fixes limit overshoot on Tensor trap node

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rap; offer 100 pcap + 100 5 (2022/2023) drivings

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ps re-probing (fixes stop-then-reset sawtooth at the limit)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… a working stop switch that discharges (charge_stop_level 100 5, negative current) is no longer wrongly rejected; now it locks and holds

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…OR discharge), drop the idle filter + once-per-session sentinel, retry until locked. Finally locks charge_stop_level on discharge-only A16 Pixel

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
seyedehsanhadi and others added 17 commits June 2, 2026 13:13
… + prioritize_batt_idle_mode=no) so auto-lock grabs the working current_max switch -- charge_stop_level is dead on A16, usb/current_max 0 is the real stop

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…s (usb/main-charger/constant_charge_current/input_current wildcards)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…g-blip fix + deep sleep (VR-25#293) + install busybox/daemon fallbacks (VR-25#216 etc)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…7) + all-paths current-cut group switch + write-config param hardening

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The `acca -s key=value` write path (used by every front-end config change)
re-exec'd itself through a BARE `setsid`. On busybox builds without setsid the
re-exec returned 127 and `set -e` aborted before write-config ran -- the write
silently no-opped. Read commands never touch setsid, so reads worked while
writes did nothing. `exec 4<>$0` self-flock was a second failure on read-only
module dirs.

Fix: `-s` writes synchronously now (accd re-reads config.txt every loop, so the
writer need not detach), with a guarded best-effort flock on a tmpfs lock that
is skipped when flock is absent. Front-ends get a real exit code.

This also unblocks the Scan & lock scripts: --apply locks via `acca -s
"s=... --"`, which silently failed pre-rc14 on setsid-less devices.

Also sync customize.sh + META-INF update-binary to install.sh (they had drifted
to the pre-rc12 installer, missing the busybox self-healing fallbacks).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…n first over-limit charge

When the battery reaches your limit but charging has not stopped (no switch is
actually capping on this phone), the daemon now auto-runs the tested switch
scanner (acc-switch-scan.sh --apply) ONCE per breach episode -- detached,
self-healing -- which finds the switch that stops charging and locks it
(charging_switch=<best> --). The lock lives in config.txt, which ACC owns, so it
persists across reboot. If the config is reset or ACC is reinstalled, the lock
is gone and the next over-limit charge re-detects + re-locks automatically. No
user action, no AccA needed.

- Opt out with `touch /data/adb/vr25/acc-data/.no-autolock` (falls back to the
  old notify-only nudge).
- Never overrides a switch the user locked manually (trailing " --").
- Bundles the rc14 acca -s setsid fix (which the --apply lock depends on).

module.prop 202505205 -> 202505206.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Turns the auto scan+lock into a guaranteed X..Y contract and applies the full
stress-test patch set. Charging-behavior changes are fail-safe and additive.

Daemon (accd.sh, misc-functions.sh):
- Charge-state correctness: reset the whole auto-lock campaign whenever charging
  stops/unplugs, so a transient plug/unplug blip is never mistaken for "charging
  past the limit" and the next real charge starts a clean detection.
- Debounced trigger: only act when PLUGGED + over-limit + still-charging, confirmed
  over a short re-check.
- Runtime CONTRACT MONITOR: if a LOCKED switch lets the cap breach for 3 confirmed
  loops, unlock it, blacklist it for the session, and re-detect another. cycle_switches
  honors that blacklist so the daemon's own locker won't re-pick a failing switch.
  This makes the range a guarantee, not a one-shot guess.
- Bounded auto-detect: max 3 scans per episode, then a LOUD give-up notification --
  never loops, never goes silently uncapped (audit bug).
- Crash recovery: clear transient markers on (re)start; keep attempt-count/blacklist.
- Mode passed to the scanner from config (Hold@Limit iff allow_idle_above_pcap).

Scanner (acc-switch-scan.sh):
- INPUT/charger-current sensing -> classify each stop as bypass (true hold, charger
  still feeds phone) / clean / DRAIN (charger dead, battery powers load). DRAIN
  switches are REJECTED from locking (kills the Sony/passthrough silent-drain).
- RESUME verification: a switch must stop AND verifiably re-arm to rank top
  (guarantees the cycle can recharge from X). Unverified ones are downranked.
- Single-instance flock mutex (auto vs manual scan), thermal-throttle warning,
  blacklist-skip, PD-renegotiation tolerance (wait widened), no-switch marker.
- Default method = Range Cycle; Hold@Limit (bypass) is the opt-in.

Coverage (ctrl-files.sh): add OnePlus/OPPO mmi_charging_enable and Samsung
batt_full_capacity (SOC-relative). Default config: allow_idle_above_pcap=false
(Range Cycle is the default; set true for Hold@Limit).

Decision logic state-machine tested (classify, ranking, attempt-cap, give-up,
contract monitor + blacklist). On-device verification still required.

module.prop 202505206 -> 202505207.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…reboot fix) + recovery + per-switch diagnostics

ROOT CAUSE of "another scan already running" + "doesn't charge until reboot"
(Pixel 9a, multi-switch device): the rc16 breach watchdog SPAWNED the external
acc-switch-scan.sh --apply from inside the daemon loop. That scanner `acca -D
stop`s the daemon and toggles switches in a detached process Android can kill; a
kill skips its restore trap, leaving a current node (e.g. usb/current_max) pinned
at 0 = NO CHARGE until reboot, and it holds the scan lock so the user's manual
"Scan & lock" aborts with "another scan already running". The daemon ALSO auto-
locks in-process via cycle_switches_off, so the spawn was redundant AND a race.

Fix A (accd.sh): the daemon NEVER spawns the scanner. cycle_switches_off (its own
current-verified in-process locker) is the only auto path; the breach watchdog now
only monitors (unlock+blacklist a locked switch that stops holding) and surfaces
state (bounded notify -> loud give-up). No daemon stop, no detached process, no
scan-lock contention, no race.

Fix B (accd.sh): startup recovery -- when plugged, restore all candidate switches
to ON once (subshell-isolated cycle_switches on), so a node a killed scan left at
0 is un-pinned and charging recovers via AccA "restart daemon", NO reboot needed.

Fix C (acc-switch-scan.sh): cleanup now restores EVERY switch to ON (restore_all_on),
not just the in-flight one, so a graceful exit/error never leaves a node pinned off.
Writes per-switch results to logs/switch-test.log (WORKS/DRAINS/NOEFFECT/SKIP).

(AccA rc19 surfaces switch-test.log + working-switches.log in diagnostics so you can
see which method works/doesn't on each phone.)

module.prop 202505207 -> 202505208.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…charge_start_level)

THE fix for Pixel 9a (and any google,charger device): confirmed on-device that the
generic switch model fails here -- current_max=0 does NOT stop Tensor charging (4.59A
flowed with all current_max nodes at 0, SOC above limit), and charge_stop_level as a
TOGGLE either drains (off=5) or fights the firmware. ACC also never set
charge_start_level at all. The NATIVE firmware pair works perfectly (user confirmed:
charge_stop_level=40 + charge_start_level=39 -> holds idle at 40%, no overshoot, no
drain) -- this is what the 2023-era ACC did.

rc20: when google,charger/charge_stop_level + charge_start_level both exist, the daemon
DRIVES THE NATIVE PAIR from pause/resume_capacity (charge_stop_level=pause,
charge_start_level=resume) and SKIPS the on/off toggle entirely -- the firmware holds at
the limit and resumes at the start level. Re-syncs every loop so AccA limit changes apply
live; forces a pause (lowers the stop level) at/above max_temp; sets the levels at startup
instead of cycle_switches-on (no 100-overshoot). Opt out for the generic logic:
touch /data/adb/vr25/acc-data/.no-native-limit

sync_native_limit logic unit-tested (cool/hot/limit-change). module.prop 202505208 -> 209.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…led write)

write-config.sh wrote config.txt with `> $config` (truncate-in-place) then appended
scripts/help with `>>`. The daemon re-reads config.txt every loop, so it could read a
half-written file; and a failed/partial write (disk full, permission loss) left config.txt
truncated while acca -s still reported success. Now the full config is written to
$config.tmp and ATOMICALLY renamed (mv) into place -- the daemon always sees a complete
file, and a failed write leaves the previous config intact.

module.prop 202505209 -> 202505210.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…erted limits)

From the final pre-stable audit (findings verified vs code; theoretical ones rejected):
- capacity-mask divide-by-zero guarded: skip masking when pause_capacity == shutdown_capacity
  (the masked formula's denominator is pause-shutdown).
- write-config now enforces shutdown_capacity < resume_capacity (%-mode): an inverted config
  could otherwise shut the phone down at a high level or never pause.
- native limit clamped to [0..100] before writing charge_stop_level / charge_start_level, so a
  bad/out-of-range config value can never be written raw to the firmware nodes.

module.prop 202505210 -> 202505211.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Identical tested code; drops the -rc label. versionCode stays 202505211 (in lockstep with the
AccA bundle). Stable.6 line: native Pixel/Tensor charge_stop_level limit, all-paths group switch,
brick-safe switch probing, atomic config writes + audit safety bounds, deep sleep, install
robustness, acca --state export.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ted-current kernels

Some kernels (e.g. Motorola XT2301) report current_now inverted (charging negative). The
scan's 'nr < 0 = stopped' test false-positived every switch there. Anchor stop-detection to a
reversal vs the charging baseline's own sign (chgNeg/revd); Pixel negative-discharge path
unchanged. Scan-only; charge-limiting (capacity/online based) was never affected. NOT released
-- private test build acc_v2025.5.18-stable.6.1.zip for one Motorola tester.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ority, sign+unit-robust auto-lock, classification + online-node + spurious-shutdown fixes)

From a deep multi-agent audit across SoC families. Order-preserving switch-list de-dup (was
sort -u -> alphabetized, clobbering curated priority). Daemon anti-flicker check made
sign-agnostic + unit-scaled (matches the scan fix): mA and inverted-sign kernels no longer
false-lock; Pixel discharge-hold preserved. Scan inFile: drop static usb/current_max, add
dc/wireless live nodes. online_f: + main-charger/oplus/glink. Spurious-shutdown guard on
garbage shutdown_temp. All sh -n clean + adversarially reviewed SHIP. AccA needs no control
change. versionCode 202505212.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The environment deleted accd.sh, misc-functions.sh, acc-switch-scan.sh, batt-interface.sh and
module.prop from the working tree before the stable.6.1 commit, so that commit recorded their
DELETION. The published acc_v2025.5.18-stable.6.1.zip asset was built before the wipe and is
correct; these files are recovered verbatim from it (hardening intact: order-preserving de-dup,
sign-agnostic+unit-scaled auto-lock, scan classification, online-node, spurious-shutdown guard).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
rc20 delegated resume to the google,charger firmware via charge_start_level,
but the Tensor driver latches "stopped" and ignores it (upstream bug). After
the limit was hit, re-plugging did nothing; only a reboot restored charging.

native_unlatch detects the latched state (plugged + at/below resume_capacity,
or a fresh plug-in below the limit, while the kernel still reports not-charging)
and pulses charge_stop_level=100 — the only value that re-arms the FET — then
sync_native_limit restores the real stop (no overshoot). Self-disables on
healthy firmware, never fires in the [resume..limit] idle band (no sawtooth),
cannot overcharge. Non-Pixel (nativeLimit=false) paths untouched.

Verified by an 8-case mock-sysfs regression harness.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
6.2 fixed Pixel/Tensor (native_unlatch). 6.3 extends it to generic devices
(Motorola/Qualcomm/etc.) whose switch (input_suspend, current_max=0,
charging_enabled=0) holds "off" across an unplug/replug — after the limit was
hit, re-plugging did nothing until resume_capacity, or a reboot on latching
switches (the reported Motorola symptom: stops at limit, won't resume on replug).

- shared loop-top plug tracker (freshPlug/wasOnline) drives both paths
- native_unlatch refactored to use freshPlug
- generic_rearm: fresh plug below limit -> enable_charging; one-shot (no
  sawtooth), skips boot loop (.minCapMax) to respect off_mid_charge, online-gated

Verified by a 13-case mock-sysfs harness; original logic harness 17/17 unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.

2 participants