testsAndMisc/linux_configuration/scripts/mtk_root/NOTES.md
Krzysztof kuhy Rudnicki 1b43ba093c
Some checks are pending
Pre-commit checks / pre-commit (push) Waiting to run
Shell tests / shell-tests (push) Waiting to run
Shell tests / arch-tests (push) Waiting to run
mtk_root: build the rooting toolkit before the phone lands
Prep for a Ulefone Armor X12 Pro (MediaTek MT6765) that has not arrived,
validated read-only against the Pixel 6a that has.

Partition discovery never hardcodes boot vs init_boot. It reads
/dev/block/by-name/, derives the slot scheme from observed _a suffixes, and
picks the ramdisk carrier by preference order init_boot then boot over what is
actually present. The AOSP launch-version rule explains why that ordering is
right; the code consults the filesystem, not the API level. One path therefore
serves both a Pixel (no init_boot, falls to boot_a) and a 13-launch MediaTek
device (init_boot_a taken). Patching the wrong one yields a phone that will
not boot.

Classification refuses rather than guesses: ULEFONE needs a positive MediaTek
signal AND a vendor match, with no "not a Pixel therefore Ulefone" fallback.
Carrier lock is always reported UNDETERMINED, because sys.oem_unlock_allowed,
ro.oem_unlock_supported and settings global oem_unlock_allowed read identically
whether a device is carrier-locked or merely has the toggle off; asserting a
negative would wrongly rule out a path.

No flashing script, deliberately. Automating irreversible partition writes
defeats the confirmation steps that catch a wrong partition or missing slot
suffix.

Three bugs found by running the code rather than only linting it:
- SIGPIPE: `adb version | head -1` under pipefail exited 141 and killed
  00-preflight.sh mid-run
- 20-dump-stock.sh exited silently when no recon had run, because find on a
  nonexistent cache dir fails under set -e
- the read-only static scan had a hole: stripping all quoted strings to avoid
  false positives on printf text also hid `adb shell "reboot bootloader"`.
  Now only printf/echo/cat arguments are stripped, and a negative self-test
  asserts the scan fails on injected commands

Also narrowed a world-writable udev catch-all (idVendor=="*", MODE="0666")
that granted every local process read/write access to every USB device;
commented with a backup, adb connectivity re-verified afterwards.

Validated: 58/58 offline fixture tests; 00-preflight.sh HOST READY (12/12);
10-recon.sh read-only against the Pixel reports PIXEL, boot_a, A/B, no
init_boot, path C-or-D indeterminate, with the device unchanged afterwards.
Untested and labelled as such in READY.md: the init_boot branch on real
hardware, mtkclient over BROM, and the whole flash/verify sequence.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018ReVsw7qovYkkGbqcU9tn7
2026-08-04 20:38:44 +02:00

5.3 KiB

mtk_root — build notes, surprises, and mistakes

Written 2026-08-04, while preparing for a Ulefone Armor X12 Pro that had not yet arrived.

Installed versions (this host, 2026-08-04)

Tool Version
adb 1.0.41 (android-tools)
fastboot 37.0.0-android-tools
python3 3.14.6
shellcheck present
shfmt 3.13.1
systemd / udevadm 261 (udevadm verify available)
mtkclient rev 0542a87, 2026-08-02
Magisk v30.7

Magisk provenance

Kept here as well as in ~/.cache/mtk-root/stock/README.md, because the cache is not version-controlled and vanishes with a cleanup.

field value
version v30.7 (released 2026-02-23)
asset Magisk-v30.7.apk
sha256 e0d32d2123532860f97123d927b1bb86c4e08e6fd8a48bfc6b5bee0afae9ebd5
size 11613864 bytes

Trap: the same GitHub release also publishes app-debug.apk (~25 MB), and a naive "first .apk asset" match picks it. That is the debug build, not the one to install. I hit this during the fetch. Match the filename exactly.

Re-check before use:

curl -fsSL https://api.github.com/repos/topjohnwu/Magisk/releases/latest \
  | grep -E '"(tag_name|browser_download_url)"'

Surprises

1. The Ulefone is probably the init_boot branch, not boot. The Armor X12 Pro launched on Android 13 (Aug 2023). Per AOSP, devices launching on 13+ carry the ramdisk in init_boot, so the Ulefone is likely the opposite branch from the Pixel used to validate this toolkit. The Pixel run therefore does not exercise the code path the Ulefone will need. Fixtures cover it; hardware has not.

2. The mtkclient venv on this host was broken, not missing. ~/.cache/bl9000-root/mtkclient/venv existed and looked complete, but every import usb failed under Python 3.14 — a Python upgrade had orphaned it. This is why 00-preflight.sh tests importability rather than directory existence: the failure mode that costs an evening is the one that looks fine on disk.

3. /etc/udev/rules.d/51-android.rules had a world-writable catch-all. ATTR{idVendor}=="*", ATTR{idProduct}=="*", MODE="0666" grants every local process read/write access to every USB device — storage, security keys, not just phones. Commented out on 2026-08-04 via install.sh --fix-udev; backup at 51-android.rules.bak-20260804T183038Z. adb connectivity was re-verified afterwards. Note 51-pinephone.rules still sets MODE="0666", but scoped to specific devices rather than a wildcard.

4. SIGPIPE killed the preflight script. adb version | head -1 under set -o pipefail exits 141 when head closes the pipe. The script aborted mid-run on its very first execution. Version strings are now captured whole and sliced with parameter expansion.

5. A test fix silently punched a hole in the read-only guard. To stop the static scan tripping on the word "fastboot" inside printf text, I first stripped all quoted strings — which also hid mtk_adb shell "reboot bootloader" from every check. The guard protecting a daily-driver phone was weaker after the "fix" than before it. Now only the arguments of printf/echo/cat are stripped, and test_read_only_scan_catches_injection asserts the scan actually fails on injected commands.

6. 20-dump-stock.sh exited silently when no recon had run. find on a nonexistent cache dir fails under set -e, so the script printed its title and stopped with no explanation. Guarded; it now says which command to run first.

Assumptions that need checking against the real device

  • Ulefone property values are guessed. ro.product.manufacturer=Ulefone, ro.product.model=Armor X12 Pro, ro.board.platform=mt6765. If the unit reports otherwise, classify_device returns UNKNOWN and everything refuses — safe, but it will look like a bug. Run ./10-recon.sh --explain-classification, then widen the patterns at the top of ../lib/mtk_common.sh.
  • Path semantics A/B/C/D are inferred. The companion document defining them was never provided, so 10-recon.sh prints an assumption banner every run.
  • The Ulefone USB vendor ID is unknown. udev/60-mtk-root.rules covers MediaTek 0e8d; if the device enumerates under a Ulefone-specific VID, add it (lsusb with the phone attached).
  • mtkclient's install steps may drift. install.sh falls back to pip install <clone> when requirements.txt is absent, but read the current README if the venv build ever fails.

Verified good news

mtkclient rev 0542a87 has an explicit MT6765 entry in mtkclient/config/brom_config.py (name="MT6765/MT8768t", loader mt6765_payload.bin) — the Ulefone's exact chipset is supported. This does not establish that this particular unit's BROM is accessible: SLA/DAA authentication state is per-device and undocumented.