Running CipherOS (Android 14) on the Rabbit R1 with persistent root services via Magisk. Includes workarounds for the R1's broken touchscreen suspend, non-functional auto-rotation sensor, and non-standard fastboot implementation.
The Rabbit R1 has several hardware/firmware issues when running custom ROMs:
| Problem | Cause | Solution |
|---|---|---|
| Touch dies on screen off | Hynitron touch IC enters deep sleep on fb_blank and fails to wake |
grab_power4 — intercepts power button, toggles backlight instead of suspending |
| No auto-rotation | MTK DEVICE_ORIENTATION sensor returns PERMISSION_DENIED | auto_rotate — polls accelerometer directly, sets rotation via settings |
| Standard fastboot hangs | MTK USB implementation incompatible with standard fastboot CLI | pyusb_fastboot.py — communicates via raw USB bulk transfers |
| No volume buttons | Hardware lacks volume keys, can't use button combos | Enter fastboot via adb reboot bootloader only |
- Linux host with
adbinstalled aarch64-linux-gnu-gcccross-compiler (for building from source)python3withpyusb(for flashing)- Magisk installed on the device
- mtkclient (for recovery/initial flash)
make # builds all binaries (static ARM64)
make deploy # pushes binaries + scripts to device via ADB
make install-service # also installs the Magisk boot script# Push and install a single binary
adb push grab_power4 /sdcard/Download/
adb shell "su -c 'cp /sdcard/Download/grab_power4 /data/local/tmp/ && chmod 755 /data/local/tmp/grab_power4'"
# Install the boot script
adb push r1_services.sh /sdcard/Download/
adb shell "su -c 'cp /sdcard/Download/r1_services.sh /data/adb/service.d/ && chmod 755 /data/adb/service.d/r1_services.sh'"Grabs exclusive access to both input devices that report KEY_POWER (/dev/input/event0 and /dev/input/event1), preventing Android from entering suspend.
- Short press: toggles LCD backlight between 0 and saved brightness
- Long press (5s): triggers shutdown via
svc power shutdown - Holds a wakelock to keep CPU alive
The touchscreen (Hynitron on I2C bus 4) dies permanently when Android calls fb_blank because the driver's fb_notifier puts the IC into deep sleep, and the resume sequence fails to wake it. Only a cold boot (full power cycle) recovers it. This workaround avoids fb_blank entirely.
grab_power3 is an older version kept as a fallback.
Polls the accelerometer via dumpsys sensorservice every 250ms and sets user_rotation directly through Android settings.
- Disables Android's broken
accelerometer_rotationsetting - Calculates orientation from tilt angle (requires >25 degrees from horizontal)
- Applies hysteresis (2 consecutive readings) to prevent flickering
- Ignores readings when device is flat or in motion
auto_rotate.sh is the original shell prototype (superseded by the C version for lower overhead).
The R1 has a motorized pop-up camera controlled via sysfs (/sys/devices/platform/step_motor_ms35774/orientation). This script polls the foreground activity and automatically raises the camera (180) when a camera app is active, and stows it (90) otherwise.
Pulses GPIO 174 (Hynitron reset pin, active low) via /dev/gpiochip0 to attempt a touch controller reset. This alone doesn't fully fix the suspend bug (the driver doesn't re-run I2C initialization after reset), but it's useful for debugging.
Standard fastboot CLI hangs on MediaTek devices with USB ID 0e8d:201c. This script uses PyUSB to send fastboot commands via raw bulk transfers.
# Enter fastboot
adb reboot bootloader
# Query device
python3 pyusb_fastboot.py getvar current-slot
python3 pyusb_fastboot.py getvar slot-count
# Flash a boot image
python3 pyusb_fastboot.py flash boot_a boot_magisk_patched.img
# Set active slot and reboot
python3 pyusb_fastboot.py set_active a
python3 pyusb_fastboot.py rebootInstalled to /data/adb/service.d/, runs at boot after sys.boot_completed. Starts all services and configures screen-on settings (stay_on_while_plugged_in, screen_off_timeout=MAX_INT). Also enables ADB over WiFi on port 5555.
| File | Purpose |
|---|---|
grab_power4.c |
Power button handler (active) |
grab_power3.c |
Power button handler (fallback) |
auto_rotate.c |
Auto-rotation daemon (C) |
auto_rotate.sh |
Auto-rotation daemon (shell prototype) |
touch_reset.c |
GPIO touch reset utility |
cam_watch.sh |
Camera motor controller |
r1_services.sh |
Magisk boot script |
pyusb_fastboot.py |
PyUSB-based fastboot for MTK devices |
Makefile |
Build and deploy |
udev-rules/ |
Linux udev rules for MediaTek USB devices |
| Path | Purpose |
|---|---|
/data/adb/service.d/r1_services.sh |
Magisk boot script |
/data/local/tmp/grab_power4 |
Power button handler |
/data/local/tmp/auto_rotate |
Auto-rotation daemon |
/data/local/tmp/cam_watch.sh |
Camera motor controller |
sudo modprobe -r cdc_acm # prevent kernel from claiming device
cd /path/to/mtkclient
python mtk.py w boot_a /path/to/boot_magisk_patched.imgThe device is unprotected (no SBC/SLA/DAA), so mtkclient works without auth.
Cold boot only — full power off, then power on. Warm reboot does not reset the PMIC regulator that powers the touch IC.
If the MT6357 PMIC enters a protection state (e.g., from dual shutdown signals), the device appears completely dead with no USB enumeration. Let the battery fully drain (leave overnight), then recharge on a wall charger. It will enter preloader bootloop, and you can flash via mtkclient.
- SoC: MediaTek MT6765 / Helio P35
- PMIC: MediaTek MT6357CRV
- Touch IC: Hynitron (I2C bus 4, addr 0x1a)
- Camera motor: Stepper motor via sysfs (
90=down,180=up) - USB IDs:
0e8d:2000(preloader),0e8d:201c(fastboot/ADB) - Input devices:
event0(mtk-kpd),event1(mtk-pmic-keys),event2(scroll wheel),event3(touchscreen)
- Never run
adb root— triggers USB re-enumeration which kills touch - Never unbind platform/I2C drivers at runtime — causes bootloop
- Never send dual shutdown signals — causes PMIC latchup (device appears dead)
- Always cold boot if touch dies — warm reboot won't fix it
MIT