How it works
What BooxUltimatum asks of the tablet, part by part, with the file it lives in and the test behind it.
- Device
- BOOX Note Air6 COne unit, the author's own
- Firmware
- 4.3, Android 16
2026-08-17_12-29_4.3-rel_0817_fd4f8e9fe - Status
- Verified here, untested elsewhereBoox interfaces are unofficial here; an update can change them
Access tiers, and why no root
Every feature and tweak declares the least access it needs, and the app degrades gracefully without it. The tier is part of each tweak's definition in code, and the app checks it before offering the change.
| Tier | What it is | What it unlocks |
|---|---|---|
| T0 | Just the app, as installed | Home screen, sleep screen (Sleep image mode) and power-off screen, Instant ink, battery measurement, the tablet-wide font, the settings hub, and becoming the home screen through Android's own confirmation. For restricted apps, links to Android's battery page and Boox's hidden App Freeze page. |
| T1 | Permissions granted once from a computer with adb | Reading battery statistics in the app, showing or hiding status bar icons tablet-wide, and setting-based tweaks |
| T2 | Shizuku: a small service with the rights of the shell user (uid 2000) | Most tweaks, reading and lifting Boox's background restriction for every app, one-tap home switching, the network name in the header, Sleep now, the Over Transparent sleep mode |
| T3 | Root | Out of scope, by decision on 2026-09-25 |
The one-off T1 grants:
adb shell pm grant app.booxultimatum android.permission.WRITE_SECURE_SETTINGS
adb shell pm grant app.booxultimatum android.permission.DUMP
adb shell pm grant app.booxultimatum android.permission.READ_LOGS
adb shell appops set app.booxultimatum GET_USAGE_STATS allow
The repository bundles these as scripts: tools/host/grant-permissions.ps1 (or .sh) grants T1 once, and tools/host/start-shizuku.ps1 (or .sh) starts Shizuku after each reboot. Shizuku needs adb to start, and firmware 4.3 hides the Wireless debugging switch, so for now that takes a computer.
Tablet-wide, the status bar can have icons shown or hidden (icon_blacklist, T1). The custom Wi-Fi and battery designs are drawn in the home screen's own header: replacing the system's icon artwork needs a system overlay, which the shell can't create (cmd overlay fabricate answers "must be root").
For T2, the app binds a Shizuku user service (ShellService, over AIDL) that runs commands as the shell user. That helper costs about 66 MB, and as a home screen the app process lives forever, so the service is unbound and its process removed after 45 seconds without calls. Shizuku itself stops on every reboot. With the tablet plugged into a computer, tools\host\start-shizuku.ps1 restarts it; firmware 4.3 hides Wireless debugging, so restarting it from the tablet alone isn't possible yet.
Why no root
On older Boox models the bootloader shipped unlocked. On this one it doesn't: the capture reads verifiedbootstate=green, flash.locked=1, vbmeta.device_state=locked, with ro.oem_unlock_supported empty and SELinux enforcing. No public firehose loader is known for its Qualcomm QCS6690 either. Root isn't a given, so on 2026-09-25 the project decided to target T0 to T2 and nothing above. Nothing is flashed, nothing touches the system partitions, and the bootloader stays locked.
The limit is real and sometimes costs a feature. Android 16 refuses shell writes to DeviceConfig flags outside its allowlist, so a planned Enter deep sleep sooner tweak was dropped rather than shipped broken.
In the repository
- app/…/core/PrivilegeStatus.ktWhich grants and which tier the app has right now.
- app/…/core/exec/Privileged.ktThe Shizuku executor and its 45-second release.
- app/…/core/exec/ShellService.ktThe user service that runs as the shell user.
- docs/00-device-research.md§0: the facts verified on the device, boot security included.
- tools/host/start-shizuku.ps1Restarting Shizuku from a computer after a reboot.
The home screen's resource budget
The launcher had one hard requirement besides being good: it must never compete with the Boox launcher for memory or CPU. Measured on 2026-09-26 with dumpsys meminfo and top -d 15 on home:
| BooxUltimatum home | Boox home (com.onyx) | |
|---|---|---|
| Memory (PSS), in front | 64–132 MB, about 60 MB of it window buffers just after start | 455 MB |
| Memory (PSS), behind | 29–44 MB | n/a |
| CPU while idle on home | 0.0 % | 0.2 % |
The figures come from choices, not from luck:
- No polling. Receivers and the network callback are registered only between
onStartandonStop; the page refreshes onACTION_TIME_TICK, battery and connectivity broadcasts while visible, and package callbacks. - An 8 MB icon cache, trimmed when the UI is hidden.
- The wallpaper decoded at half resolution in
RGB_565, and only when the file changes. - Widget hosting that listens only while visible; weather refreshed only while visible and at most hourly.
- The Shizuku helper released after 45 seconds of idle.
Compatible by construction
It uses only public launcher APIs (LauncherApps for apps, shortcuts and badged icons, AppWidgetHost for widgets), with no reflection and no hidden APIs. Recents belong to com.android.onyxquickstep, and gestures, NaviBall and EinkWise to SystemUI, so they behave the same under either home. The launcher ships disabled as a HOME alias, the Boox home is journaled before the first switch, and from a computer this always brings it back:
adb shell cmd package set-home-activity --user 0 com.onyx/.StartupActivity
Verified as the default home on the tablet: Home and Back, app shortcuts, the Boox entry points (Library, Notes, Storage), Recents, and recovery after the process is killed.
In the repository
- docs/05-launcher.mdCompatibility rules, what is built, the budget table.
- app/…/launcher/LauncherActivity.ktThe home activity and its start/stop lifecycle.
- app/…/launcher/AppCatalog.ktThe app list through
LauncherApps, and the Boox functions. - knowledge/experiments.mdThe 2026-09-26 rows: memory, CPU, the Shizuku helper, default home.
The sleep screen
Boox's sleep screen is a doze dream, com.onyx/com.onyx.common.dream.OnyxDaydreamService. Its style and pictures live in com.onyx's private MMKV, which no other app can read or write. But the always-running com.onyx process has a dynamic, exported receiver that needs no permission, and Boox Settings uses it too:
am broadcast -a onyx.action.SCREENSAVER \
--ei type 16 \
--es file '/storage/emulated/0/Pictures/booxultimatum-sleep.png' \
--ez show_result_hint false
Type 16 switches the style to Image, with that file as its only picture. This broadcast is part of Onyx's public SDK (ScreenSaverUtils, used by OnyxAndroidDemo's screensaver sample), and other open-source apps such as KOReader cover tools use it too. The app sends it from its own uid, which was verified on the tablet, so Sleep image mode is T0; with Shizuku it repeats it from the shell. Restore sends type 16 with Boox's own /system/media/standby-1.png, because the previous style can't be read back, and offers Boox's screensaver settings beside it. The app never sends it while the tablet is asleep, because other developers report that doing so blanks the sleep screen to white; a switch that falls due then waits for the next wake.
The same broadcast with type 17 sets the power-off picture. BooxUltimatum renders the chosen face in portrait, without the battery and put-down time that would be stale by the next power-on, and sends it on request. Boox saves its own copy (logcat: SaveShutdownImageAction: Save shutdown image succeed, for both the normal and the charging variant), so it changes only when set again; restore sends Boox's /system/media/shutdown-default.png.
Read fresh at every sleep, never while asleep
A file directly in /sdcard/Pictures/ is used in place and decoded afresh each time the tablet goes to sleep, so overwriting it between sleeps changes the next sleep screen. A file anywhere else is copied once and later overwrites are ignored. Files above 10 MB are dropped. Once asleep, nothing is read again, and Boox itself only redraws its own clock overlay, every 5 minutes. So by default the face is kept current while the tablet is awake, and only then:
- While awakeA non-wakeup inexact alarm (
ELAPSED_REALTIME) every 1, 5, 15 or 30 minutes, plus battery, time, date and screen-on broadcasts the system sends anyway. Coalesced for 2 s. - Each refreshThe face's content and settings are fingerprinted; unchanged means no render, no encode, no write. The other orientation is rendered too and kept ready.
- Going to sleep
ONYX_SYSTEM_arrives tens of milliseconds before Boox decodes. If the picture doesn't match the rotation, the ready one is written at once.GOING_TO_ SLEEP - AsleepNothing is read. The alarm never fires; a missed one is delivered on wake. Unless live updates are on, below.
Live updates while asleep New
The sleep screen is Android's doze dream, com.onyx/.common.dream.OnyxDaydreamService, fixed by config_dozeComponent. It never re-reads the picture, but it registers an exported receiver for onyx_dream_refresh: any app can send it, and the dream wakes the display for about 1.2 s (onyx.action.DISPLAY_CHANGED_STATE, state 2, then 3). Frames drawn while it dozes are held until then. The lock screen hides every app window except an accessibility overlay, so an accessibility service that reads nothing holds the face.
- Going to sleep4 s after Onyx has drawn and dozed, the first live face goes up, over the charging bar too.
- Every stepAn exact
RTC_WAKEUPalarm on the round minute: render, send the refresh, show the face when Onyx reports the display on. About 1.4 s awake. A full repaint every sixth update. - WakingThe overlay is removed at once, so the lock screen and PIN pad are never covered.
Boox's power manager (android.onyx.pm.OnyxAlarmHelper) clears wake-up alarms at sleep unless the app is on its full-access list, which it joins when its background use changes to allowed. Verified on battery on firmware 4.3; the battery cost is still to be measured.
Both orientations, prepared
Boox centre-crops the picture to the rotation it sleeps in: 1860 × 2480 in portrait, 2480 × 1860 at rotation 90 or 270. A face drawn only after a turn left a few seconds in which a sleep showed the old shape cropped. Every refresh therefore also encodes the other orientation into noBackupFilesDir/sleep-cache, and a rotation, seen through a display listener before the configuration reaches the process, swaps that file in at once, with a fresh render 1.5 s later. Tested by rotating and sleeping within 0.2 s in both directions: the picture was written 100 ms after Boox's going-to-sleep broadcast and 260 ms before the dream decoded it.
Boox's bar while charging
Boox draws a status bar (battery, “Press power button to wake up”) along the bottom of any sleep screen unless Screensaver › Bottom Status Bar is off, and always while the tablet charges. In com.onyx the check is isShowBottomStatusBar() || isBatteryCharging(), and it reads the real charger, so dumpsys battery unplug doesn't hide it. Faces don't reserve the strip; the studio asks owners to turn the bar off and says why it still shows on the charger.
Over Transparent is T2
Boox's Transparent style snapshots the screen and can lay a sticker PNG over it, saved as Pictures/Sticker/sticker_<timestamp>.png and reloaded at every sleep. The shell user can overwrite it and the app can't, so this mode copies BooxUltimatum's plate over the newest sticker through Shizuku, after backing up and journaling Boox's own.
Still to confirm on the tablet: that MediaStore overwrites made with mode "wt" are picked up at the next sleep, the Boox clock zone used for the layout guide (the top 22 %), and render and encode times per face at full size.
In the repository
- docs/06-sleep-screen.mdHow Boox draws the sleep screen, modes and tiers, and the refresh design.
- app/…/core/sleep/SleepPublisher.ktThe MediaStore picture, the broadcast, the sticker copy and its backup.
- app/…/core/sleep/SleepScheduler.ktThe non-wakeup alarm and the event receivers.
- app/…/core/sleep/SleepStudio.ktRender, fingerprint skip, encode, publish, the orientation cache.
- app/…/core/sleep/SleepRenderer.ktPure Canvas typesetting, with no Context and no I/O.
- knowledge/experiments.mdThe sleep-screen rows: the broadcast, file rules, crop, sticker, the bar.
Instant ink
ExperimentalBoox Notes gets its ink under the nib in about 10 ms because SurfaceFlinger draws the stroke itself. Its touchReader and touchConsumer threads read the pen's event node and push small HANDWRITE partial updates to the e-paper driver (/dev/ebc, open only to root and SurfaceFlinger). Apps don't draw that ink; they only arm it, with private ISurfaceComposer transactions for the pen state, the region, the stroke and the repaint.
This is a documented Onyx feature. App developers get it through Onyx's pen SDK (onyxsdk-pen, TouchHelper), which turns it on inside the calling app's own view. Under the hood the SDK calls the firmware helper android.onyx.ViewUpdateHelper, after lifting Android's hidden-API check with double reflection. Two things are not in the SDK, and they are what Instant ink adds: arming the path for another app that doesn't include the SDK, and handing the stroke back to that app after the lift. On firmware 4.3 (Android 16) the SDK's route is also closed to third-party apps (both calls are denied as api=blocked), so BooxUltimatum tries the firmware helper first and otherwise sends the same SurfaceFlinger transactions itself.
Any process can arm that path for whichever app is in front. BooxUltimatum does it from its ordinary app uid; the Shizuku route in the code is only a fallback for firmware that refuses the direct one. The other app is untouched and still receives every pen event.
One stroke
- ReadyAs soon as Instant ink is on, a session is opened and paused at once. On firmware 4.3 a session opened as the pen arrives misses the stroke that follows, while a paused one resumes instantly, even at the touch.
- HoverThis pen reports hover as
BTN_TOOL_BRUSH, only about 45 ms beforeBTN_TOUCHon a quick stroke, and a quick first touch comes in the same batch. The app in front is read then, from usage events. If it's chosen, the session resumes and, as Onyx's SDK does at hover,ENABLE_POSTwith 0 holds the app's own frames back, so the first point of the stroke lands on a held screen. - TouchSurfaceFlinger draws the stroke, and holds the app's frames by itself from the touch (its
HandlePenTrigger) until they're let through. The app still gets every pen event. Letting its frames through mid-stroke ends the preview for the rest of the stroke, so the app's drawing always waits for the lift. - Lift + 500 ms
ENABLE_POSTwith 1 lets the frames through, and the app's real stroke replaces the preview. The pen session stays open. - Pen awaySwap at once and pause the session, so nothing of ours draws if the next app isn't a chosen one. The eraser end pauses it too, and so does the screen turning off. Over Boox's own apps, which run sessions of their own, it never ends a session.
A tap with the pen often opens another app while the pen stays in range, so the app in front is read again a few times after a tap. Keeping one session open is the point. Closing it and repainting also removes the preview, but a session reopened at pen-down lost the start of the stroke, and one reopened at hover missed quick strokes. HANDWRITING_REPAINT, REPAINT_EVERY_THING, SAVE_PEN_ATTACHED_FB, the auto-sync switch and the screen-note pipe were all tried one call at a time on a stuck preview, and none of them removed it. ENABLE_POST did, even as a quick hold-and-release when nothing had held the frames.
The region lives in the panel's landscape frame
SurfaceFlinger reads the handwriting region in the panel's own landscape frame, whatever the rotation. A portrait-shaped region of 1860 × 2480 gave no preview in the bottom quarter of a portrait screen, below y = 1860. So the region is always a square as large as the long side, 2480 × 2480, which covers the panel in every orientation.
| Transaction | Code | Used for |
|---|---|---|
SET_PEN_STATE | 16711693 | Start, draw, pause or stop the session, with the owner pid |
SET_REGION_LIMIT | 16711694 | The square region, long side by long side |
SET_STROKE_STYLE / WIDTH / COLOR | 16711688 / 87 / 86 | Sent in that order, after the pen state goes to start: choosing a style resets the width to that style's default, so a width sent first was lost. 4 px, black, fountain pen (style 1) by default. |
ENABLE_POST | 16711692 | Hold app frames (0) and release them (1): the swap |
GET_PEN_STATE | 1048643 | Probe the route, and read the session state |
REPAINT_EVERYTHING | 16711700 | Only in release, handing the panel back to normal drawing |
The pen's event node (/dev/input/event5, onyx_emp_istaric_pen) is readable by apps on this firmware, but its name isn't: SELinux closes /sys/class/input to apps. So the reader opens every readable node, takes the first that reports a pen tool and remembers it. The reader thread sleeps in poll() until the pen moves, so nothing wakes the tablet, and a 30-second watchdog never leaves app frames held back.
What isn't done yet
- The mechanism was found in test sessions from the shell and an in-app prototype, with the owner drawing in Sketchbook. The in-app service is built and starts cleanly, but hasn't yet been checked with a real pen.
- The preview is Boox's black pen, so a coloured brush or the eraser only shows once the app's stroke takes over.
- A stroke could lose the first few millimetres of its preview, about one in ten. Holding the app's frames from hover instead of pen-down fixed it in the owner's test on 2026-09-27; it's worth watching in other apps.
- Pen strokes can't be simulated:
inputinjects through InputDispatcher, which SurfaceFlinger's pen reader doesn't read. Every test needs someone drawing.
A second route, EinkWise's per-app handwriting mode (noteConfig), was tried on Sketchbook and didn't engage. Its config was written back byte for byte, and it isn't shipped.
In the repository
- app/…/core/ink/InstantInk.ktThe service: arm on hover, hold at touch, swap after the lift.
- app/…/core/ink/SurfaceInk.ktThe firmware-helper route, and the SurfaceFlinger transactions it falls back to.
- app/…/core/ink/PenInput.ktThe pen's event node, read in
poll(): hover, touch, eraser. - knowledge/experiments.mdThe Instant ink rows: the path,
ENABLE_POST, the region, hover.
The battery log
A tool that measures drain mustn't cause it. The log has no foreground service and no wakelock. It samples on AlarmManager.setInexactRepeating with ELAPSED_REALTIME, the non-wakeup variant, every 30 minutes: the system batches it with other work and only delivers it when the tablet is already awake, so it adds no wakeups at all.
The alarm is set only when it's absent. Re-scheduling an inexact repeating alarm restarts its countdown, which dumpsys alarm showed before and after opening the app, so scheduling on every open could have kept the log from ever sampling.
What a row holds
- Light samples: level, charge counter in mAh, voltage, temperature, plugged, screen on, elapsed realtime and uptime, boot count, current, front light, Wi-Fi, Doze and Battery Saver. Between two samples, 1 − Δuptime/Δelapsed is the share of time the tablet spent suspended.
- Transition rows:
screen_on,screen_off,plug,unplug,level(a 1 % step on battery),doze_deep,doze_lightandsaver, from broadcasts the system sends anyway. Standby and use are split exactly. - Deep snapshots every 3 hours: Doze state, alarm wakeups by app and tag, wakelocks, CPU and Wi-Fi by uid, foreground time.
- Opening the app adds a sample, at most one every 10 minutes.
Android 15 and later send BOOT_COMPLETED to an app the first time it starts after a force stop, so the log once recorded 33 “boot” rows without a single reboot. It now compares Settings.Global.BOOT_COUNT and records those as unstop. Files are CSV and JSONL in Android/data/app.booxultimatum/files/logs, pruned after 60 days, and shared as one zip with a README that explains the columns.
What it found so far
| Finding | How it was read |
|---|---|
| Standby is already near the floor: 0–12 mA. Boox's own sleep turns Wi-Fi off within 0.5 s and suspends the processor within 30 s. | batterystats history, two unplugged sleeps |
| Screen-on use drew 430–640 mA (12–17 %/h), with the processor awake 93 % of the screen-on time. | dumpsys batterystats since unplug |
TIME_TICK is a wakeup alarm on this firmware, and sys.onyx.idledelay=2500 lets the processor sleep between page turns when nothing else keeps it up. | dumpsys alarm, getprop |
Doze starts disabled at every boot (config_enableAutoPowerModes=false). Turning it on changed little: no Doze transition was seen in two unplugged sleeps. | aapt2 dump resources, dumpsys deviceidle |
BOOXDrop (com.onyx.easytransfer) had used 8 min 27 s of CPU in 4.5 h of uptime, the most of any Onyx process. | top, TIME+ column |
No before-and-after battery runs are recorded yet, and the first overnight unplugged run is still to do. Until they are, the app shows no savings. For comparison only, eWritable measured about 2 %/h idle and 10 %/h writing on the same 4.3 firmware.
In the repository
- app/…/core/BatteryLog.ktThe alarm, the transition receiver, boot versus unstop, the export.
- docs/03-architecture.mdThe power sampler design: must not cause the drain it measures.
- knowledge/experiments.mdBattery A/B runs (none yet) and the findings above.
- tools/host/power-logger.ps1The host-side logger for lab runs, with zero app code involved.
The tablet font
Boox Settings › Display › Font Style changes the system font without root through a broadcast that SystemUI handles. BooxUltimatum sends the same one:
am broadcast -a onyx.action.font.replace.system \
--ei font_lang 0 \
--es args_path '/storage/emulated/0/fonts/<family>.ttf'
SystemUI writes the persist.sys.font.* properties and hot-reloads the font in every app, with no reboot. onyx.action.font.reset.default returns to the Boox default. The interface was learned by decompiling ChangeSystemFontAction and BroadcastHelper in Boox's own settings package, then confirmed from the shell and from the app with getprop before and after.
- The font file is copied to
/sdcard/fontsthrough Shizuku, where SystemUI can read it. That's why the tablet-wide font is T2. NeoReader reads the same folder. - The path in use before the first change is journaled, once.
- The broadcast is sent, then the property is read back for up to five seconds. Success is only reported once the tablet has actually switched.
- Restore previous sends the journaled path again, or the reset broadcast when it was the Boox default.
One surprise: the owner's system font was a variable Manrope, and Android hands apps its default instance, which is ExtraLight, so everything drawn in the system font looked thin. The home screen now loads the tablet font with explicit wght instances, one step heavier by default, and Use on the whole tablet comes with a weight choice.
Reversible tweaks and the journal
A tweak is a small class with an id, a group, a tier, a risk, a way to read its state, an apply and a revert. Apply and revert never throw; failures come back as a readable message, and both outcomes are written to the journal.
- The original is kept once. Before changing anything, a tweak stores the previous values only if none are stored yet, so applying twice never loses what the tablet had. The write is synchronous, because some changes (fonts, overlays) restart the process straight after.
- Undo only undoes what the app did. A tweak that restores a recorded value offers Undo only when BooxUltimatum made the change. If the tablet already had that value, undoing would invent a state the owner never had.
- Preset means the firmware already ships the tweak's value: the app shows it as on and offers no undo.
- The journal lives in the app's private preferences, keeps the last 200 entries, and never leaves the device.
Round trip below means applied on the tablet, the state re-read as on, undone, and the original confirmed.
| Tweak | Tier | Mechanism | Status |
|---|---|---|---|
apps.background | T2 | RUN_ANY_IN_BACKGROUND allow for user apps Boox restricted | Round trip 2026-09-25 |
doze.enable | T2 | dumpsys deviceidle enable | Round trip 2026-09-25; resets on reboot |
doze.boox_allowlist | T2 | Non-essential Onyx apps off the Doze allowlist | Round trip still to record |
bg.boox_restrict | T2 | RUN_ANY_IN_BACKGROUND ignore for optional Onyx apps | Round trip 2026-09-25 |
power.saver | T2 | cmd power set-mode 1 | Applies only once unplugged |
power.adaptive | T1 | App standby and adaptive battery on | Preset |
power.autosync | T0 | Master auto-sync off | Round trip still to record |
power.timeout | T2 | screen_off_timeout = 2 minutes | Round trip 2026-09-25 |
power.stay_awake | T1 | stay_on_while_plugged_in = 0 | Preset |
radio.wifi_scan | T1 | wifi_scan_always_enabled = 0 | Preset |
radio.ble_scan | T1 | ble_scan_always_enabled = 0 | Round trip 2026-09-25 |
radio.wifi_wakeup | T1 | wifi_wakeup_enabled = 0 | Round trip 2026-09-25 |
radio.location | T2 | Location off | Preset |
ui.serif_font | T2 | Noto Serif font overlay | Round trip 2026-09-26 |
ui.animations | T1 | Animation scales = 0 | Preset |
privacy.dns | T1 | Private DNS to dns.adguard-dns.com | Round trip 2026-09-25 |
privacy.ota | T2 | pm disable-user the OTA service | Round trip still to record |
privacy.store | T2 | pm disable-user the two Boox stores | Round trip 2026-09-25 |
privacy.factory | T2 | pm disable-user the factory test app | Preset |
Outside the list, and journaled the same way: status-bar icons (icon_blacklist), the battery percentage, the default home app, the tablet font, the sleep screen, and fonts copied for NeoReader. Package tweaks use pm disable-user, which is reversible; nothing is ever removed from the system partitions.
Clean-room interoperability
BooxUltimatum contains no Onyx or BOOX code, SDKs or assets. The Boox interfaces it uses were learned from Onyx's public SDK documentation, by observing the tablet, and by reading how Boox's own apps call the firmware:
- the sleep-picture and system-font broadcasts, which aren't publicly documented;
- the SurfaceFlinger handwriting and post transactions behind Instant ink, which Onyx's pen SDK documents for an app's own views;
- the EinkWise configuration it reads.
Only the facts are used. They are called through small clients written from scratch in core/sleep/, core/SystemFont.kt and core/ink/. Onyx's SDK itself, onyxsdk-pen and onyxsdk-device, is published as obfuscated binaries without source, so it isn't bundled. Because these interfaces aren't a stable public API, a firmware update can change them without notice; when something stops working, the app says so rather than guessing.
The dependencies are AndroidX and Jetpack Compose and the Shizuku API, all Apache-2.0. The one bundled asset is Archivo, under the SIL Open Font License. The app goes online for two things only: weather from Open-Meteo for the city you type, and Google Fonts when you open the fonts browser. There are no accounts, analytics, ads or trackers.