Device: Motorola moto XT2435 (MediaTek MT6855). Also reproduces on Motorola XT2533 (Qualcomm Snapdragon SM7435).
Android version: 14 (also reproduced on Android 15)
Magisk version name: 30.7 (debug build, reports 30.7:MAGISK:D)
Magisk version code: 30700
Reproduced on a debug build with no modules installed.
Summary
On Motorola devices, off-mode charging (device powered off, plugged into a charger, showing the charging animation) runs a near-full boot that reaches post-fs-data, so magiskd increments the bootloop counter — but a charger boot never reaches boot-complete, so the counter is never reset. Two off-mode-charging boots with no full boot in between raise the counter to 2, and the next normal boot falsely enters Safe Mode (Zygisk turned off, all modules disabled). This happens from ordinary charging-while-off, not a real bootloop.
Root cause (native/src/core/bootstages.rs)
let boot_cnt = self.get_db_setting(DbEntryKey::BootloopCount); // read
self.set_db_setting(DbEntryKey::BootloopCount, boot_cnt + 1)…; // increment
let safe_mode = boot_cnt >= 2 || …; // trigger
The counter is cleared only at boot-complete:
// Reset the bootloop counter once we have boot-complete
self.set_db_setting(DbEntryKey::BootloopCount, 0).log_ok();
On Motorola, off-mode charging is implemented as a near-full boot: the bootloader shows its normal (unlocked) warning screen, then the patched boot image + init run through post-fs-data with /data mounted, so magiskd runs post_fs_data (ro.bootmode=charger) and increments BootloopCount. It then stays at the charger UI and never reaches boot-complete, so the counter is never reset. There is no charger/ro.bootmode guard anywhere in the boot-stage code.
Evidence (debug v30.7, no modules)
Two consecutive off-mode-charging boots, ro.bootmode=charger, counter (bootloop DB value) never reset:
charger boot #1 : ro.bootmode=charger bootloop=1
charger boot #2 : ro.bootmode=charger bootloop=2
getprop persist.sys.boot.reason.history:
reboot,charger → reboot,charger → cold,powerkey
The next normal boot (magisk.log):
Magisk 30.7(30700) daemon started
** post-fs-data mode running
* Initializing Magisk environment
* Safe mode triggered
** boot-complete triggered
Result: zygisk=0, all modules disabled.
Why it is OEM-dependent, not SoC / not "low battery"
Reproduced on both a MediaTek (MT6855) and a Qualcomm (Snapdragon SM7435) Motorola, so it is not chip-specific — it is Motorola's off-mode-charging implementation running the full init path (mounts /data, runs post-fs-data). OEMs whose off-mode charging uses a lightweight path never run magiskd, so the counter never moves and they are unaffected.
Steps to reproduce (stock/official build, Motorola)
- Boot normally:
magisk --sqlite "SELECT value FROM settings WHERE key='bootloop'" → 0.
- Power off. Plug in charger → charging animation (off-mode charging).
- Unplug (device powers off), plug in again → charging animation. Do this ≥2 times with no boot to system in between.
- Power on into the OS →
magisk.log shows * Safe mode triggered; Zygisk off; modules disabled.
Suggested fix
Don't count charger-mode boots toward bootloop protection. In post_fs_data:
if get_prop(cstr!("ro.bootmode")) != "charger" {
let boot_cnt = self.get_db_setting(DbEntryKey::BootloopCount);
self.set_db_setting(DbEntryKey::BootloopCount, boot_cnt + 1)…;
// safe_mode check …
}
i.e. treat androidboot.mode=charger boots as not counting toward bootloop, since they never reach boot-complete by design.
Related
Likely the same root cause as #9530 (closed with no mechanism identified there).
Device: Motorola moto XT2435 (MediaTek MT6855). Also reproduces on Motorola XT2533 (Qualcomm Snapdragon SM7435).
Android version: 14 (also reproduced on Android 15)
Magisk version name: 30.7 (debug build, reports
30.7:MAGISK:D)Magisk version code: 30700
Reproduced on a debug build with no modules installed.
Summary
On Motorola devices, off-mode charging (device powered off, plugged into a charger, showing the charging animation) runs a near-full boot that reaches
post-fs-data, somagiskdincrements the bootloop counter — but a charger boot never reaches boot-complete, so the counter is never reset. Two off-mode-charging boots with no full boot in between raise the counter to 2, and the next normal boot falsely enters Safe Mode (Zygisk turned off, all modules disabled). This happens from ordinary charging-while-off, not a real bootloop.Root cause (
native/src/core/bootstages.rs)The counter is cleared only at boot-complete:
On Motorola, off-mode charging is implemented as a near-full boot: the bootloader shows its normal (unlocked) warning screen, then the patched boot image + init run through
post-fs-datawith/datamounted, somagiskdrunspost_fs_data(ro.bootmode=charger) and incrementsBootloopCount. It then stays at the charger UI and never reaches boot-complete, so the counter is never reset. There is nocharger/ro.bootmodeguard anywhere in the boot-stage code.Evidence (debug v30.7, no modules)
Two consecutive off-mode-charging boots,
ro.bootmode=charger, counter (bootloop DB value) never reset:getprop persist.sys.boot.reason.history:The next normal boot (
magisk.log):Result:
zygisk=0, all modules disabled.Why it is OEM-dependent, not SoC / not "low battery"
Reproduced on both a MediaTek (MT6855) and a Qualcomm (Snapdragon SM7435) Motorola, so it is not chip-specific — it is Motorola's off-mode-charging implementation running the full init path (mounts
/data, runspost-fs-data). OEMs whose off-mode charging uses a lightweight path never runmagiskd, so the counter never moves and they are unaffected.Steps to reproduce (stock/official build, Motorola)
magisk --sqlite "SELECT value FROM settings WHERE key='bootloop'"→0.magisk.logshows* Safe mode triggered; Zygisk off; modules disabled.Suggested fix
Don't count charger-mode boots toward bootloop protection. In
post_fs_data:i.e. treat
androidboot.mode=chargerboots as not counting toward bootloop, since they never reach boot-complete by design.Related
Likely the same root cause as #9530 (closed with no mechanism identified there).