Skip to content

kendrick90/Mabu

Repository files navigation

Mabu liberation toolkit

Reverse-engineering and repurposing the Mabu health robot tablet (Catalia Health, defunct). The robot is an Android 8.1 tablet bonded to a motor controller and power daughterboard, locked down by Esper MDM (kiosk mode, Device Owner, USB ADB suppressed).

This repo is the validated procedure for removing that lockdown — turning the tablet back into a freely user-controlled Android device while keeping the option to drive the robot motors via the original factory-test app.

Quick start (1 command per unit)

# Connect via internal USB harness, power on, catch Loader (PID 0x320A)
.\scripts\flash-mabu-v3.ps1
# WiFi setup happens on the touch UI when prompted
# Done. Lawnchair + F-Droid + Mabu Factory Mode installed,
# pre-wipe-archive/unit-<serial>/ has APKs + sdcard + dumpsys.

flash-mabu-v3.ps1 is the canonical protocol: it does everything flash-mabu.ps1 does plus a pre-wipe capture pass (preserves per-unit APKs, sdcard contents, and runtime state before the /data reformat). Magisk root is not attempted by default -- both Magisk v30.7 and v27.0 produce boot images that hang at recovery "no command" on this RK3288 H7R boot.img layout. See notes/magisk-incompatibility.md. We don't need root for any actual project capability -- /dev/ttyS1 is crwxrwxrwx (shell/app can drive motors directly), and Loader-side /system writes cover anything else.

That's the whole assembly-line flow. The rest of this README is what's inside the box.

Hardware

  • SoC: Rockchip RK3288 (confirmed via PID 0x320A + chip-info). Board ID HRA7_RK3288W_V1.2_2021.10.15.
  • eMMC: Samsung 16 GB BGA, ext4 throughout, 30,310,400 × 512 B sectors.
  • Android: 8.1.0, build fingerprint rockchip/H7R/H7R:8.1.0/OPM6.171019.030.E1/...:user/release-keys. Same build on every unit we've seen.
  • USB OTG: Broken out via a 30-pin header on the main board (see the pinout below). D+/D− polarity was the gotcha to remember during first-time wiring.

30-pin header pinout (2 × 15)

2 mm pitch (2 × 15, dual-row). Pin 1 location TBD on first inspection.

Col A Pin Pin Col B
DCIN 1 2 DCIN
GND 3 4 DCIN
GND 5 6 GND
SPKN 7 8 SPKP
GND 9 10 GND
RTS 11 12 CTS
TX 13 14 SDA
RX 15 16 SCL
GND 17 18 GND
PWRON 19 20 IN3P
VCCUSB 21 22 PDM
OTG_ID 23 24 VCC
OTG_DM 25 26 GND
OTG_DP 27 28 ADKEY
GND 29 30 GND

Functional groups: USB OTG (VCCUSB / OTG_ID / OTG_DM / OTG_DP / GND), motor UART (TX / RX / RTS / CTS), PMIC I²C (SDA / SCL), audio (SPKN/SPKP differential out, PDM mic, IN3P aux), buttons (PWRON, ADKEY resistor ladder via ADC).

Software stack

  • Esper (Device Owner / kiosk) at provisioning time, packaged as io.shoonya.shoonyadpc (DPC, installed to /data/app/), io.shoonya.helper and com.shoonyaos.oculus.plugin.supervisor.h7r (in /system/app/), plus io.esper.remoteviewer and io.esper.otamanager (in /data/app/).
  • Catalia Mabu Factory Mode (com.catalia.factorymode): the factory-test program with the main class com.catalia.mabu.navigation.MainActivity. Misleading class name — it's the diagnostic suite, not the consumer Mabu conversational app. The consumer app was never archived; presumed Esper-deployed only.

What the patches actually do

Eight sector-level writes via Rockchip Loader. None of them touch boot.img; they target raw eMMC sectors in /system, the parameter partition, and the adbd binary.

# Where What Why
1 parameter @ LBA 0 Kernel cmdline: androidboot.veritymode=disabled androidboot.selinux=permissive, Rockchip CRC32 recomputed Removes dm-verity and SELinux enforcement so /system edits can take effect at runtime
2 /system/bin/adbd @ LBA 1,696,240 Byte 284: 0x01 → 0x00 (auth_required global) adbd accepts host without dialog/key approval
3 /system/bin/adbd @ LBA 1,694,778 Bytes 56-57: F0 B5 → 70 47 (BX LR — return early from adbd_auth_init) Defense in depth
4 /system/app/espersupervisor.apk @ LBA 1,851,238 Zero the 4-byte EOCD signature PackageManager skips
5 /system/app/esperdpc.apk @ LBA 1,981,802 Same Same
6 /system/app/esperhelper.apk @ LBA 2,063,565 Same Same
7 /system/bin/set-device-owner.sh @ LBA 1,691,408 Zero the 4 KB data block Init runs an empty shell script (no-op) instead of dpm set-device-owner …
8 /system/etc/init/init.esper.rc @ LBA 2,076,672 Zero the 4 KB data block Init parses an empty .rc — no set-device-owner service ever registered

The /data wipe (wipe-data-head.ps1) is separate. It is required on active-Esper units because the Device Policy Controller binary (io.shoonya.shoonyadpc) is installed to /data/app/ by Esper at provisioning time, not to /system/. The /system EOCD nukes cannot reach it, and DPM blocks soft uninstall (SecurityException: Attempt to remove non-test admin). 96 MB of zeros at the head of the partition corrupts the ext4 superblock; vold detects this on boot and reformats /data cleanly. Larger wipes (256+ MB) have correlated with a Settings.apk Developer Options crash on unit 1; smaller wipes don't reliably trigger reformat. 96 MB is the sweet spot.

The patches survive factory reset (they're in /system or sector 0). WiFi credentials, motor calibration, and any installed user apps are wiped by /data reformat.

Per-unit state matrix (after liberation)

Unit Serial DO Esper USB ADB WiFi ADB ADB auth Disposition
1 2022010502079 clear clean wedges 10.0.0.161 dev-mode (no auth) Donor board: power board reused for the USB/Loader programming harness. Dev Options crashes; cause unknown despite byte-identical /system with units 2-4. Deferred — may revisit via full firmware dump-and-replace if it ever stops being a donor
2 2022010500480 clear clean works 10.0.0.147 ✅ restored Returning to original owner (friend who lent unit 1). Currently mostly disassembled
3 2022010501476 clear clean works 10.0.0.252 ✅ restored Working in body; antenna seating was finicky during reassembly but final reads were the best of the session (WiFi −62 dBm, LTE RSRP −97 dBm). SIM present
4 2022010501557 clear clean works 10.0.0.69 ✅ restored Primary unit going forward. Cleanest of the fleet, fully provisioned via the unified script, deploy-ready
5 2022010501537 clear clean works 10.0.0.117 dev-mode Flashed via the polling Loader catch path. OpenCV Manager + factorymode + animations installed
6 2022010500003 clear clean works TBD dev-mode First V3 run. Pre-wipe capture preserved 4 APKs + 17.8 MB /sdcard including new com.catalia.mabu.softkeyboard.apk not seen on any prior unit. Magisk attempts (v30.7 and v27.0) both produced boot images that hang at recovery "no command" -- restored original boot.img via Loader. Confirmed Magisk-on-this-hardware is a dead end; documented in notes/magisk-incompatibility.md

Caveats / known limits

  • No consumer Mabu app. Our archives only contain factorymode. The patient-facing Mabu conversational software is not in any captured /data on any of three units — presumed Esper-deployed only at provisioning time, never persisted across factory reset.
  • Dev Options crashes on unit 1. Cause unknown. /system, /vendor, Settings.apk, Settings.odex, sepolicy binaries, kernel boot args, and all ro.boot.* props are byte-identical between unit 1 and units 2/3/4 where Dev Options works. /data was wiped fresh on both unit 1 and unit 4 via the same 96 MB Loader-side procedure; unit 1 still crashes, unit 4 doesn't. The exact crash is an SELinux denial: avc: denied { read } for name="u:object_r:logpersistd_logging_prop:s0" scontext=u:r:system_app:s0 ... permissive=0 triggered by DevelopmentSettings.setLogpersistOff() -> SystemProperties.set() in onResume. Why this only triggers on unit 1 with identical bytes everywhere is an open mystery — possibly /persist or another OEM partition we haven't probed, or hardware-baked SELinux load order. Workaround: use ADB shell for anything Dev Options would do. Patching Settings.apk dex requires the platform signing key (we don't have).
  • USB ADB unreliable on unit 1. Enumerates, then sits as offline forever. Cause unknown. WiFi ADB is the stable transport on this build regardless — the parameter file already includes service.adb.tcp.port=5555.
  • Loader read wedge. rkdeveloptool's rl works in 4 MB chunks but the Loader wedges after ~28 MB cumulative reads in one session, requiring a physical power cycle. The cycled dumper (scripts/dump-system-cycled.ps1) is the workaround. The current procedure avoids large reads entirely, so this is only relevant if firmware ever drifts and we need to capture a fresh /system image.

Layout

  • scripts/ — live scripts. See scripts/README.md for what each one does.
  • scripts/archive/ — dead-end attempts (boot.img repacking, pyusb rockusb clients, OTA sideloading, etc.) with explanations of why they didn't work.
  • notes/HANDOFF.md — detailed session-by-session log. Has more depth than this README; consult it when something here doesn't match observed behavior.
  • notes/partition-table.md, loader-readout.md, etc. — reference data.
  • apks/ — committed installers (F-Droid, Lawnchair, factorymode is in mabu-archive/).
  • mabu-archive/ — per-unit captures (Mabu APKs, sdcard animation CSVs, dumpsys outputs). The factorymode.apk + animations source for restore.
  • tools/ — downloaded tooling (rkdeveloptool, Rockchip drivers, Zadig). Gitignored.
  • firmware/ — captured + modified eMMC bytes. patches/ and originals/ are committed (scripts can't run without them); system-probes/ and scratch/ are gitignored. See firmware/README.md for the full inventory.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors