ChargeSpeedAdjuster generates relaxed Xiaomi charging thermal profiles from the files supplied by the currently installed OS. It does not keep stock backups and does not copy generated files into Xiaomi's real configuration directory.
- Magisk runs
post-fs-data.shafter/datais available. edit.shmounts the immutable vendor, ODM, and root block devices read-only, bypassing all active Magisk overlays.- The generator inventories current immutable
thermal-*.confsources using ODM, vendor, then system precedence. AES block candidates must pass strict decryption and exact re-encryption; adjacent plaintext control files remain untouched. - The source manifest, build identity, converter, and patcher are hashed.
- Changed sources are decrypted, schema-validated, patched, encrypted, and decrypted again to prove an exact crypto round trip.
- During installation, the validated files are staged under
system/vendor/etc, reproducing v4.2's Magisk vendor-overlay coverage. - During early boot, the overlay backing files are refreshed in place and the
complete validated output directory is bind-mounted over
/data/vendor/thermal/config. After Kitsune establishes its magic mounts, a late read-only check verifies every generated file byte-for-byte at/vendor/etc. This works whether Xiaomi's real writable profile directory is empty or already populated. - After the overlays are verified,
service.shenables Xiaomi's wired and wireless thermal-removal controls, validates their readback, and listens for charging-path events so it can reassert a value if Xiaomi resets it.
If generation or early vendor-backing refresh fails, the writable profile
directory is not mounted. The late visible-overlay audit records any mismatch
in runtime/vendor-overlay.log. Magisk overlays and bind mounts disappear on
reboot, so disabling or removing the module restores the real files
automatically.
On a phone previously modified by v4.2, the real /data files may already
contain v4.2's permanent copies. This redesign never writes another restoration
over them. It generates clean mounted profiles from immutable current sources;
an OS/vendor reset of the writable thermal cache is the safe way to remove that
pre-existing legacy state.
Only charging-specific sections are considered. A section must have one of the known schemas:
algo_type monitorwithdevice batteryalgo_type sicwithdevice thermal_fcc_overridealgo_type monitorwithdevice wireless_charge
The patcher retains v4.2 behavior where appropriate, but replaces its disabled FCC/SIC controller with an explicit relaxed curve:
- In
monitor/batterysections, the firsttrigandclrthreshold lists are replaced by emptytrigandclrfields. - In
sic/thermal_fcc_overridesections, the stock controller is replaced with the following battery-side current curve. Power figures use 4.3 V and are approximate; input power and displayed charger power are different.
| Virtual temperature band | Target | FCC range | Approx. battery power |
|---|---|---|---|
| Below 40 C | Unregulated by this profile | Up to 20.9 A | Up to 90 W |
| 40-41.3 C | 40.5 C | 9.3-16.28 A | 50-70 W |
| 41.3-43.5 C | 42.5 C | 6.98-13.95 A | 40-60 W |
| 43.5-44.5 C | 44 C | 4.65-11.63 A | 30-50 W |
| 44.5-47 C | 45 C | 3.5-9.3 A | 15-40 W |
| 47 C and above | 47 C | 2.33-6.98 A | 10-30 W |
Each transition clears 0.5 C below its trigger to avoid rapid band changes.
- In
monitor/wireless_chargesections, stock mitigation states use a dedicated wireless temperature curve:
| Virtual temperature trigger | Wireless mitigation state |
|---|---|
| 42 C | 0 |
| 43.3 C | 705 |
| 44.5 C | 1008 |
| 45.5 C | 1413 |
| 47 C | 1515 |
Each state clears 0.5 C below its trigger. These packed firmware states are
not watts. Based on live measurements, state 0 is the least restrictive,
705/1008 held about 37 W, and 1413/1515 held about 32 W.
1515 is the strongest state observed in the current stock profiles, so
FCC/SIC supplies the additional battery-current reduction at 47 C and above.
The temperatures used here come from Xiaomi's composite VIRTUAL-SENSOR0, not
necessarily the battery temperature shown by Android. Unknown charging schemas
fail generation instead of being guessed. CPU, GPU, modem, display, and
emergency platform thermal controls are not modified.
Xiaomi's charging firmware also exposes independent wired and wireless thermal
votes outside the encrypted profiles. The module sets both removal controls to
1; device testing shows that the mapped userspace wireless monitor continues
to apply its wlscharge_control_limit states independently:
/sys/class/qcom-battery/thermal_remove
/sys/class/qcom-battery/wls_thermal_remove
Xiaomi can reset these controls when a charging path is detached or
reconfigured. The service blocks on kernel USB/wireless power-supply events;
it has no periodic polling timer and holds no wakelock. On a relevant event it
checks immediately, then after one and three seconds to catch a delayed reset,
and restores both controls to 1 only when needed. If the module is disabled,
the event handler no longer reapplies the controls. Uninstall stops the
listener and restores the controls to 0 immediately.
StepChgJeit is the battery firmware's temperature-, voltage-, and
state-of-charge-dependent FCC/FV curve. On the tested Xiaomi 14 (houji),
writing 1 to /sys/class/qcom-battery/remove_temp_limit does not bypass that
curve: it makes the reported battery temperature jump to 50 C and causes
StepChgJeit to impose its hot-battery FCC/FV limits. The module therefore
leaves this property at its default 0; disabling or uninstalling the module
also restores it to 0. Separate platform emergency thermal controls remain
untouched.
The generated audit files are stored under the module's runtime directory:
source-manifest.txt: filename, selected source layer, and source SHA-256source.signature: cache identity for the complete current source setpatch-manifest.txt: every patched or unchanged file and sectiongenerator.log: generation and mount decisionsvendor-overlay.log: late byte-for-byte verification of/vendor/etccharge-controls.log: driver thermal-removal writes and readback validation
If early profile generation/mounting, late vendor-overlay verification,
charging-control setup, or event-time control restoration fails, the Magisk
module description is prefixed with [MODULE INACTIVE]. Each boot stage owns
its failure state, so a later successful stage cannot hide an unrelated
failure. The marker is removed automatically after all recorded components
recover.
The legacy thermal-bat binary is used only as a development reference. It is
not run by the module and is removed from the installed module during
installation.
The Xiaomi AES-CBC converter is source-controlled in tools/miui-thermal.
Build the Android ARM64 binary with:
cd tools/miui-thermal
GOOS=android GOARCH=arm64 CGO_ENABLED=0 \
go build -trimpath -buildvcs=false -ldflags='-s -w' -o ../../miui-thermal .The converter uses the Xiaomi-compatible key and IV thermalopenssl.h, strict
PKCS#7 padding validation, atomic output writes, and mode 0755 for newly
created directories.
- Set
version=inmodule.propto the intended release tag, for exampleversion=v4.5, and commit the change. - Create and push the matching tag:
git tag v4.5followed bygit push origin v4.5.
The release workflow builds a minimal Magisk-installable ZIP, verifies its
contents, creates a SHA-256 checksum, and publishes both files in a GitHub
Release with generated release notes. A tag that does not exactly match
module.prop is rejected.