Skip to content

Latest commit

Β 

History

37 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

HiBig Zero βš‘πŸ“–

Zero-Drain Power & Hardware Manager for Bigme HiBreak (MediaTek Helio P35 / MT6765)
Author: right9code
License: PolyForm Noncommercial 1.0.0 (Personal & Non-Commercial Use Only)


πŸ“Œ Overview

HiBig Zero is a dedicated, root-level power management and hardware tuning suite built specifically for the Bigme HiBreak B6 (Black & White and Color E-ink smartphones running Android 14).

Stock Bigme firmware suffers from severe active and standby battery drain caused by runaway vendor daemons, broken MediaTek power management policies, and unrestricted background services. HiBig Zero systematically neutralizes every measured drain source, dropping active reading drain by up to 500 mW and eliminating tens of thousands of background wakeups per day.


🎨 User Interface Highlights

HiBig Zero ships with an E-ink native, strictly 1-bit monochrome interface:

  • Responsive typography β€” every label uses Android sp text sizing and the system typeface, so text follows the device font scale instead of a hardcoded width-based scaler.
  • Density-independent layout β€” all padding, margins, and dividers use dp, so the UI scales correctly across screen densities.
  • Compact responsive banner β€” a two-line status strip showing the app name, version (v1.5.0), and author (by right9code).
  • UPDATE button β€” one-tap GitHub release checker with in-app download, progress, and self-install.
  • Clean tab bar β€” tabs are visually separated from the banner and from each other, use fixed labels, and mark the active tab with an underline indicator instead of a >> prefix.
  • Collapsible sections β€” every section header (HARDWARE, DEBLOAT, NETWORK, POWER, PACKAGE MANAGER, and the Battery sections) can be tapped to collapse or expand its body.
  • Package Manager clarity β€” the second tab is now PACKAGE MANAGER with a FREEZE, UNFREEZE, OR RESTRICT description. Each app row shows the name, package, and a plain-language status line (Not frozen | background: allowed | doze optimized | usage: ...) with FREEZE/UNFREEZE and OPTIONS stacked on the right.
  • Filter and sort dropdowns β€” the six cryptic filter chips (RSTR, PROT) were replaced by a single FILTER: selector with readable options, alongside the existing SORT: selector.
  • Dry run (plan-only mode) β€” a global switch that logs every state-changing action instead of applying it, so a whole rule set can be rehearsed safely. Status cards keep reading the real device state, and the banner shows [ DRY RUN - NOTHING APPLIED ] whenever it is on. Per-feature dry runs (SHUTDOWN_TEST_MODE) still exist.
  • Charge ceiling β€” holds the battery at a configurable target (default 80%, resume 75%) by switching the MediaTek charge input off and on, so a phone that lives on a charger is not held at 100% all night. It polls only while plugged in, and charging is restored the moment the cable comes out, whenever the feature is switched off, and at boot β€” a stuck "off" switch would mean a phone that never charges, so every failure path resumes rather than stops.
  • Removed non-essential controls β€” PAGE UP/PAGE DOWN and the bulk FREEZE ALL/UNFREEZE ALL/RESTRICT USR shortcuts were removed; per-app actions remain in each row.
  • Strict 1-bit monochrome styling β€” all buttons, the search field, and the tab bar use explicit black/white bordered drawables, eliminating the system grey Material backgrounds.
  • E-ink friendly glyphs β€” emoji and pictographs in menus and buttons were replaced with ASCII-safe labels for predictable rendering.

πŸ“¦ App Installer (APPS Tab)

HiBig Zero includes a built-in App Installer that downloads, installs, and manages essential apps directly from GitHub releases β€” no Play Store or browser needed.

Managed Apps

App Source Install Type
AnyHome right9code/AnyHome Magisk system app
KOReader koreader/koreader User app
E-Ink Bro plateaukao/einkbro User app
Obsidian obsidianmd/obsidian-releases User app
MiXplorer driftywinds/mixplorer-releases User app
LocalSend localsend/localsend User app
Obtainium ImranR98/Obtainium User app

Features

  • Resume & retry β€” downloads resume from where they left off if the connection drops, with 3 retries and exponential backoff.
  • APK verification β€” file size is checked against the GitHub API before installing.
  • SELinux-safe installs β€” APKs are staged to /data/local/tmp/ with chmod 644 before pm install -r -d -g.
  • MANAGE_EXTERNAL_STORAGE β€” automatically granted to KOReader, MiXplorer, and Obsidian on Android 14.
  • Install permission β€” REQUEST_INSTALL_PACKAGES is granted to Obtainium, so it can install APKs without a per-install confirmation prompt.
  • Magisk system app support β€” AnyHome is installed as a systemless Magisk module to /system/priv-app/.
  • Version comparison β€” skips download if already on the latest version, handles -rc1/-beta suffixes.

πŸ”„ Self-Updater

Tap the UPDATE button in the header to check for new releases of HiBig Zero itself:

  • Fetches the latest release from right9code/hibigzero via GitHub API
  • Compares installed version with the latest tag
  • Shows release notes and a one-tap UPDATE NOW or REINSTALL button
  • Downloads with progress percentage, installs via root pm install
  • Handles HTTP redirects, resume on failure, and APK verification

Download trust boundaries

Updates and app installs are both fetched as root, so a misdirected download is not a cosmetic problem. Two rules are enforced by NetGuard, shared by the updater and the app installer so the policy cannot drift between them:

  • HTTPS only, GitHub hosts only β€” github.com, *.github.com and *.githubusercontent.com. Every redirect hop is followed manually and re-checked, with setInstanceFollowRedirects(false) so the HTTP stack cannot make an unchecked hop of its own. A hop that leaves the list fails the download rather than falling back to the original URL.
  • Strict host[:port] authority β€” any character that is not part of a hostname or port is refused, and ports other than 443 are rejected. This matters because URL parsers disagree: https://evil.com\@github.com/x is read as host github.com by java.net.URL (last @ wins) while other clients treat \ as / and connect to evil.com.

The updater additionally pins the signing key: a downloaded APK must be signed by the same certificate as the running app. Android enforces this at install time anyway, but checking first means a substituted download is reported as what it is instead of failing with an opaque installer error. The check fails closed.


πŸ”‹ Measured Impact & Benchmarks

Optimization Area Stock Firmware Behavior HiBig Zero Optimized Real-World Impact
PPM CPU Frequency Lock Screen-ON locked Cluster 0 to 2.068 GHz max clock Uncaps policy 7; drops idle clocks to 745–900 MHz 300–500 mW active savings
CPU Core Hotplugging All 8 cores active even when reading static pages 4 Big cores dynamically offlined (cpu4-7) 150–250 mW leakage reduction
uart2serport Crash Loop Missing script spawns error loop every 2.5s (~34,500/day) Injects clean stub; init crash-latch eliminated CPU load drops from >25 to <2
Gboard Telemetry & ML 10+ background Superpacks/WorkManager jobs in Doze Cancels jobs; restricts AppOps to foreground only Zero deep-sleep interruptions
App Standby Buckets All 200+ apps hardcoded to EXEMPTED (5) Granular bucket control (ACTIVE to RARE/RESTRICTED) Eliminates Doze bypasses
Inactivity Retention Screen suspend blocker drained battery while idle Auto-shutdown daemon powers off device to 0.00 mA Unlimited static image retention

✨ Key Features

1. ⚑ CPU Governor & MediaTek PPM Frequency Uncap

  • The Culprit: MediaTek's mtkpower HAL hard-locks PPM_POLICY_USER_LIMIT (Policy 7) whenever the display turns on, pinning Cluster 0 to 2.068 GHz and Cluster 1 to 1.515 GHz.
  • The Solution: Disables Policy 7 via /proc/ppm/policy_status and applies tuned schedutil_efficient governor scaling down to 900 MHz (Cluster 0) and 400–745 MHz (Cluster 1).

2. πŸ“– Dynamic 4-Core E-Reader Mode

  • Offlines cores 4–7 (/sys/devices/system/cpu/cpu[4-7]/online = 0) on demand.
  • Provides a buttery-smooth reading experience in KOReader or Obsidian using 4 Little cores while entirely eliminating idle leakage across the Big cluster.

3. πŸ›‘οΈ Systemless uart2serport Fix

  • Bigme's PowerStandbyManager invokes ctl.start uart2serport on every screen wake. Because /system/bin/start_uart2serport.sh was omitted from the EEA firmware, init crashed repeatedly.
  • Clears sys.init.updatable_crashing and permanently quiets flags_health_check.

4. πŸ“¦ Granular Package Freezer & Standby Bucket Manager (Tab 2)

  • Live queried package inspector with an E-ink friendly OPTIONS β–Ύ dropdown menu per app.
  • Freeze / Unfreeze: Disables bloatware via pm disable-user --user 0.
  • AppOps Restrict: Blocks background wakelocks, background execution and foreground-service starts (WAKE_LOCK, RUN_IN_BACKGROUND, RUN_ANY_IN_BACKGROUND, START_FOREGROUND), drops the app from the Doze whitelist and pins it to the RESTRICTED standby bucket. Network access is not touched: no appop exists for it.
  • Standby Bucket Selector: Seamlessly assigns apps to ACTIVE (10), WORKING_SET (20), FREQUENT (30), RARE (40), or RESTRICTED (45).
  • Doze Exemption Toggle: Live control over Android Doze whitelist (dumpsys deviceidle whitelist).

5. ⌨️ Gboard Lockdown 2.0

  • Purges the Android 14 JobScheduler queue of all Google Superpacks dictionary and telemetry sync jobs.
  • Enforces RUN_ANY_IN_BACKGROUND ignore and START_FOREGROUND ignore without affecting normal foreground typing or offline autocorrect.

6. ⏱️ Inactivity Auto-Shutdown (AlarmManager, no daemon)

  • A single AlarmManager alarm (setExactAndAllowWhileIdle) is armed on screen-off and cancelled on screen-on. No shell daemon, no polling, no wakeups β€” the timeout costs 0 mA until it fires, then powers off with reboot -p to retain the current E-ink image.
  • Configurable timeout presets (60m, 120m, 240m, 480m) or a custom value.
  • Charging guard: a plugged-in device is rescheduled instead of powered off (nothing is saved by powering off a charger). Toggle SKIP_WHILE_CHARGING.
  • Safety net: when the alarm fires the receiver re-checks PowerManager.isInteractive() and a monotonic screen-off stamp, so a killed process or a stale alarm can never power the device off mid-use. Any error reschedules β€” it never fails open into a shutdown.
  • Single authority: enabling this also disables Bigme’s own PowersaveShutDownAlarmReceiver (re-asserted every boot), so two power-off timers can never compete. SHUTDOWN_TEST_MODE logs the decision instead of performing it.

7. πŸ“Š Live Power & Hardware Monitor (Tab 3)

  • Real-time 3-second hardware polling:
    • Battery Current: Discharge/charge current in mA.
    • Battery Voltage & Temperature: Accurate readings from mt6357-gauge.
    • CPU Frequencies: Cluster 0 & Cluster 1 clock speeds and governors.
    • Online Core Detection: Real-time indicator for 8-Core vs 4-Core E-Reader mode.
    • MediaTek PPM State: Live check of Policy 7 ([UNCAPPED] vs [LOCKED]).

πŸ› οΈ System Architecture

graph TD
    App[HiBig Zero UI] --> Config[ConfigManager]
    App --> Shell[ShellUtils ProcessBuilder]
    App --> Installer[AppInstaller Engine]
    App --> Updater[Self-Updater Engine]
    
    Shell -->|Root su -c| Kernel[Linux Kernel 4.19 / MT6765]
    Shell -->|Root su -c| AndroidOS[Android 14 Framework]
    
    Installer -->|GitHub API| GitHub[GitHub Releases]
    Updater -->|GitHub API| GitHub
    
    Kernel --> PPM["/proc/ppm/policy_status (Policy 7 Uncap)"]
    Kernel --> Hotplug["/sys/devices/system/cpu/cpu4-7/online (Hotplug)"]
    Kernel --> PMIC["/sys/.../mt6357-gauge/ (Battery Current & Voltage)"]
    
    AndroidOS --> AppOps["cmd appops (RUN_ANY_IN_BACKGROUND, WAKE_LOCK)"]
    AndroidOS --> Jobs["cmd jobscheduler (Cancel Superpacks & WorkManager)"]
    AndroidOS --> Standby["am set-standby-bucket (App Standby Buckets)"]
    AndroidOS --> Doze["dumpsys deviceidle (Doze Whitelist)"]
    
    Boot[System Boot] --> BootRec[BootReceiver]
    BootRec --> Config
Loading

πŸ“œ Persistent log

Every entry the UI shows is also appended to /data/local/tmp/hibreak.log (mode 0600) by a single background writer thread. The writer batches, so a burst such as an APPLY ALL RULES pass costs one root shell rather than one per line. The payload is base64-encoded, so command output containing quotes, backslashes or dollars cannot escape into the shell. The file rotates at 256 KB, keeping the newest 128 KB.

Unlike the in-memory list it survives the process being reaped, which is the point: the boot-time rule pass, charger events and the shutdown decision stay readable afterwards. Receivers call ShellUtils.flushLog() before PendingResult.finish(), because the process can be reaped the moment finish() returns. The log popup shows the persisted tail merged with anything not yet flushed.

πŸ”’ Config and command hardening

Package names read back from /data/local/tmp end up inside su -c commands that run on every boot, so a name is attacker-controlled input as far as the shell is concerned. Two layers stop that:

  • Ownership and mode. Files root writes but the app reads back (the config, the frozen ledger, the restricted list, the active-governor marker) used to be chmod 666, because root owns them and the app has to read them. They are now handed to the app's own uid with 0600, falling back to 0644 if the chown is refused β€” which still cannot be tampered with by a non-root process. Those writes all flow through one helper, ConfigManager.secureFileTail().
  • Validated names. ConfigManager.isValidPackageName() admits no shell metacharacter, no whitespace and no leading -, and is applied where names enter a command (buildPmCmd, buildRestrictCmd, buildUnrestrictCmd, buildSetStandbyBucketCmd, buildDozeWhitelistCmd, splitPkgList) and where they are read back (loadRestrictedPkgs, loadFrozenLedger, recordFrozen). Standby buckets and governor profiles are allow-listed the same way. A rejected name yields an empty command, and execRoot/execRootAction return without spawning a shell for it.

Worth knowing: /data/local/tmp is 0771 owned by shell, so this app cannot unlink there even from a file it owns β€” the active-governor marker has to be removed via root. File.delete() failed silently on that for a long time, which left the marker behind permanently and made it useless as a "a clamp is applied" flag.

πŸ“– Screen-state durability

The sleep clamp is applied by a dynamically registered receiver, and ACTION_SCREEN_ON/OFF can only be received that way β€” they carry FLAG_RECEIVER_REGISTERED_ONLY, so a manifest receiver cannot take the job over. That makes the clamp depend on the process staying alive, and it does not.

This is not theoretical. Reproduced on device: clamp applied while asleep, kill -9 the process, press the power button β€” the screen comes on and cpu4 is still offline with scaling_max_freq still 900000. A screen-on device stuck at 400–900 MHz with half its cores dead. Launching the app did not fix it either; only toggling the screen again or rebooting would.

Two mechanisms now guarantee the repair:

  • Reconcile on every process start. HiBigApp.onCreate runs for any process start, including receiver-only ones, so the boot, power and alarm entry points are all covered by one call. If the screen is on and the marker says a clamp is applied, the user's profile is restored and the marker cleared. With no marker this is a single file stat and returns, so a normal launch costs no root shell.
  • A non-wakeup watchdog alarm, armed at screen-off and re-armed while the screen stays off, in SleepWatchdogReceiver. A PendingIntent lives in the system's alarm queue, so it survives the process being killed and restarts it to be delivered. It uses ELAPSED_REALTIME, not ..._WAKEUP: a wakeup alarm would hold the SoC out of suspend to ask a question whose answer only matters once the device is awake. An elapsed-realtime alarm never wakes the device and is delivered when it next wakes β€” exactly when the answer changes. Verified: the framework registers it as type=ELAPSED, and an overdue one is delivered about two seconds after the power key.

Measured results (kill the process while clamped, then wake):

Path Outcome
Without the fix stays clamped indefinitely
Cold start while the screen is on (any trigger) repaired
Overdue watchdog delivered on wake repaired 2 s after the power key
Normal screen off→on with the process alive unchanged, immediate

Residual window: at most one watchdog interval (90 s), and only when the process was killed and the screen comes back on before that interval expires and nothing starts the app. If the alarm has already expired it is delivered on wake, so a phone woken after a long sleep is repaired immediately rather than after 90 s.

The interval is a deliberate trade-off: a shorter one narrows the residual window but polls more often while asleep. The polling is cheap β€” non-wakeup, and Android rate-limits exact alarms during Doze, so it settles to the idle quota in deep sleep.

GovernorReconciler owns the marker file and the repair, so the semantics for "is a clamp applied" and "how is it lifted" live in one place instead of being spread across whichever receivers happen to touch it.

Note: the sleep clamp was measured on 2026-09-21 β€” see docs/SLEEP_CLAMP_TEST.md. It saves ~10–17 mA while the device is awake with the screen off, and nothing at all while suspended (where the firmware powers the cores down regardless). The clamp's mechanism is verified, but the total-energy benefit cannot be measured on this device: the battery gauge is unusable below ~10 mA (charge_counter frozen, positive current reported during discharge) and the timing/current instruments contradict each other by ~6Γ—. Because the upside is small and bounded while the failure mode is a crippled device, the recommendation is to run with SLEEP_GOVERNOR_ENABLED=0.

πŸ›‘οΈ Boot-loop guard

A rule that loops the device is the worst failure this app can produce, and the one failure the user cannot fix from inside the app β€” because the app is what is looping. So the boot receiver counts boots: a boot within FAST_BOOT_SECONDS (180 s) of the previous one is treated as a loop rather than a restart, and after MAX_FAST_BOOTS (3) in a row the boot rule pass is suspended entirely, including self-protection. "Suspended" has to mean the app touches nothing.

The count is written before any rule runs. If it were written afterwards, the one boot that matters most would be the one that never recorded it. The guard fails open: an unreadable state file restarts the streak at zero, because suspending rules on every fresh install would be far worse than missing one detection.

Suspension is surfaced in the header β€” [!!] RULES SUSPENDED: n fast boots in a row plus a RESUME RULES button β€” because a silently skipped rule pass just looks like the app stopped working. RESUME and the normal APPLY ALL RULES button do the same thing: clear the flag, reset the streak and apply. Neither goes through the boot path, so a manual apply is never counted as a boot.

⏱️ Command execution

ShellUtils.execRoot() drains stdout and stderr on their own threads and gives every command a deadline. Reading the two streams one after the other deadlocks as soon as a command fills the pipe of the stream not yet being read, and an unbounded waitFor() means a wedged su pins the calling thread forever β€” on the UI thread, a permanent freeze. redirectErrorStream(true) would also fix the deadlock but would erase the stdout/stderr split that CommandResult exposes, so both streams are pumped separately instead.

πŸ”“ Device Rooting Guide

Important

Root Access is Mandatory
HiBig Zero requires Superuser (Root) permissions via Magisk to communicate directly with MediaTek kernel sysfs nodes, uncap PPM frequency locks, dynamic hotplug CPU cores, and enforce boot rules.

If your Bigme HiBreak is not yet rooted, follow the comprehensive step-by-step guide included in this repository:

πŸ“– Bigme B6 & HiBreak β€” Complete Rooting & Unbricking Guide

Highlights from the Guide:

  • Overcoming Single-Button Quirk: Uses right9code/mtkclient with hardware watchdog auto-reboot (--reboot) to bypass the physical button limitation.
  • Full Partition Backup: How to safely dump all 48 partitions before flashing.
  • Magisk Patching: Extracting boot_a.bin, patching via Magisk Manager, and flashing back safely.
  • One-Command Unbricking: Instant recovery to stock firmware if anything goes wrong.

πŸ“₯ Installation

Requirements

  • Device: Bigme HiBreak B6 (B&W or Color)
  • OS: Android 14 (EEA / Global)
  • Root: Magisk v26+ installed (see Rooting Guide)

Steps

  1. Download the latest APK from Releases:
    HiBigZero-v1.5.0-release.apk
  2. Install via ADB:
    adb install -r HiBigZero-v1.5.0-release.apk
  3. Open HiBig Zero on your device and grant Root (Superuser) permissions when prompted by Magisk.
  4. Tweak your desired toggles or switch to E-Reader Mode on Tab 1.
  5. Use the UPDATE button in the header to check for future updates.

πŸ”¨ Building from Source

The repository includes a zero-dependency build script that compiles Java 8 source directly into Dalvik bytecode using Android SDK build-tools:

Prerequisites

  • Java Development Kit (JDK 8 or JDK 17)
  • Android SDK platforms;android-35
  • Android SDK build-tools;35.0.0 (aapt, d8, zipalign, apksigner)

Build Command

python3 build.py

The compiled, aligned, and signed APK is generated under releases/ together with its SHA-256 checksum file.

Signing key (read this before releasing)

The build is signed with a key kept outside this repository (the repo is public, so a signing key must never be committed):

~/.config/hibigzero/hibigzero-release.jks      # the key itself (chmod 600)
~/.config/hibigzero/keystore.properties        # storeFile / storePassword / keyAlias / keyPassword

Override with HIBIGZERO_KEYSTORE, HIBIGZERO_KEYSTORE_PROPS, HIBIGZERO_STOREPASS if you keep them elsewhere.

All releases must be signed with the same certificate. Android refuses to update an app whose signature changed (INSTALL_FAILED_UPDATE_INCOMPATIBLE), so a new key orphans every existing installation - users can only recover by uninstalling and reinstalling.

build.py therefore:

  • refuses to build if the keystore is missing (it will never silently mint a new identity),
  • prints and verifies the certificate fingerprint on every build,
  • prints a loud warning if the identity differs from the expected c378f6f7....

To deliberately start a brand new identity: python3 build.py --init-key (then tell your users they must reinstall once).

History: builds up to v1.3.3 were signed with a key stored in /tmp, which was lost when /tmp was cleared (cert 2238ad46...). v1.3.4 onward uses the persistent key above. Installations of v1.3.3 or older must be uninstalled once before they can be updated.


πŸ“„ License & Attribution

Copyright (c) 2026 right9code. All rights reserved.

This project is licensed under the PolyForm Noncommercial License 1.0.0 (LICENSE).

  • Personal & Educational Use: Allowed and encouraged.
  • Commercial Use: Strictly prohibited. You may not sell, monetize, incorporate into paid products, or distribute commercial forks of this software without explicit written permission from the author.

Developed with ❀️ for the E-ink community by right9code.

About

Zero-Drain Power & Hardware Manager for Bigme HiBreak (MediaTek Helio P35) on Android 14

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages