12.4. The Onyx/Boox platform layer¶
What turns a Qualcomm lagoon reference build into a Boox e-reader is mostly
implemented above the hardware bridge, in the Android platform itself. This page
catalogues that customisation as far as it is statically visible: the
system/vendor version split, the native Onyx SDK, the configuration and policy
glue, and the preinstalled Onyx apps. Coverage of application logic here is
shallow — the bulk of the Boox behaviour is compiled Java in odex/vdex’d
framework and app archives, which are not decompiled in this documentation set —
but the shape of the layer, and the one genuinely load-bearing structural fact
(the API-level split), are clear from the partition metadata.
Everything here is from the system/system_ext/product/vendor
partition listings and the extracted build properties and init scripts. No code
was executed.
12.4.1. A GSI-style Android-15 system on an Android-11 vendor¶
“The Android version” is not a single number on this device — three layers carry independent version stamps, and only the system stamp is the platform version the user sees:
Layer |
Version (authoritative property) |
Notes |
|---|---|---|
kernel |
Linux 4.19.157 |
the SM6350 |
vendor / boot |
Android 11 ( |
the |
system / system_ext / product |
Android 15 ( |
the |
So the vendor and boot images are the Android-11 lagoon BSP built for
this specific device (NoteAir5C), while the system / system_ext / product
images are a generic Android-15 Onyx system named TabBoox — inferred, from
the generic naming and Treble/GSI-style layering, to be a single unified Boox
tablet system shipped across many models on top of a per-device Android-11
vendor; no cross-device comparison was done to confirm this. This is the
structural key to several
observations elsewhere: it is why keystore2 (an Android-12+ daemon) runs over
Android-11 keymaster@4.1 (Security and DRM userspace), and why the framework is four
releases newer than the lagoon HALs it sits on.
Note
The ro.build.fingerprint string reads
ONYX/TabBoox/TabBoox:13/TKQ1.230615.001/… — a stale :13 release token
and an old Tiramisu build-ID left in the hand-built fingerprint. It is not
the version: the authoritative ro.build.version.release / …sdk are
15 / 35, corroborated by the Android-15 SELinux policy version
(202404) and the 2026-06 system patch. Read the version properties, not
the fingerprint token.
12.4.2. The native Onyx SDK¶
The Onyx-specific native libraries in /system/lib64 (a few also 32-bit):
Library |
Role (where determinable) |
|---|---|
|
the EPD update service — the userspace end of |
|
e-ink dithering and colour-filter-array / RGBW colour (The e-ink software stack (Onyx EPDC)) |
|
low-latency pen/touch input reader |
|
dictionary engines (StarDict / MDict formats — the reader’s lookup) |
|
LAME MP3 encoder (audio note / recording export) |
|
Onyx algorithm helpers (image/text processing; |
|
Onyx’s build of Tencent MMKV, the key-value store backing
|
|
a memory self-test helper |
The display and input libraries are documented in The e-ink software stack (Onyx EPDC); the rest are
reader/app-support libraries. Note there is no separate Onyx framework
.jar — the framework-level customisation is compiled into the standard
framework.jar / services.jar of the TabBoox system image (odex’d, not
examined here).
12.4.3. Inside the native Onyx SDK: static RE¶
The libraries above were catalogued by name; disassembling the e-ink/pen-related
ones (nm -D / objdump -T symbols, strings, and capstone on the
exported functions) shows what each actually does and what it does not. All
are ordinary AArch64 ELF shared objects that retain their
dynamic symbols; the C++ is demangled below. One structural correction falls out
immediately: none of the ``libonyx_*`` libraries open ``/dev/ebc``. The EPD
listener talks to a FIFO, the pen reader talks to evdev, and the two “colour”
libraries are pure bitmap helpers. The actual /dev/ebc update client lives in
the patched AOSP graphics stack (The userspace to kernel EPD update path).
12.4.3.1. The EPD event FIFO listener¶
libonyx_epd_listener.so (JNI class android/onyx/optimization/EpdEventListener,
class EpdListener, log tag lib_epd_listener) is an event forwarder, not
an update driver. Its dynamic-symbol imports are __open_2, eventfd,
poll, read, write, close — there is no ``ioctl`` and no
``mmap``. Disassembly of EpdListener::setup shows it __open_2(path,
O_RDWR) a FIFO (the failure log reads Could not open fifo: %s %s),
creates an eventfd, and fills a two-entry pollfd array (FIFO + eventfd).
readEpdEventLoopImpl then loops poll(fds, 2, -1); on POLLIN it
reads up to 1024 bytes, wraps them as a std::string and hands them to a
Java onEpdEvent(String) callback. The eventfd is the shutdown wakeup —
closeDevice writes an 8-byte token to it (“notify event fd”). The FIFO
path is passed in from Java as a jstring (nativeStart calls
GetStringUTFChars), so it is not a literal in the .so; it is the
/dev/onyx/listener FIFO that init.onyx.rc creates (The e-ink software stack (Onyx EPDC)).
So this library is a text-event channel from the framework/SurfaceFlinger to
the app, not the pixel path.
12.4.3.2. The raw pen/stylus reader¶
libonyx_pen_touch_reader.so (JNI class com/onyx/android/sdk/pen/RawInputReader,
log tags lib_touch_reader / onyx_emp) is the low-latency handwriting
input path — it reads the pen digitiser straight from evdev, bypassing the
Android InputReader. It carries a full C++ engine: TouchReader (device +
read loop), PenManager (pressure, clip regions, erase state) and a per-mode
reader family DrawReader / BtnReader / EraseReader / SideEraseReader.
TouchReader::findDevice iterates /dev/input/event0–event15 and selects
the node whose EVIOCGNAME string contains ``Wacom``, ``hanvon`` or
``onyx_emp`` (the immediates are visible in the disassembly). openDevice
then queries it with the standard evdev ioctls, recovered as request numbers:
ioctl |
request |
use |
|---|---|---|
|
|
driver version |
|
|
bus/vendor/product id |
|
|
device name (matched above) |
|
|
physical location / unique id |
|
|
|
The read loop polls (same FIFO+eventfd wakeup pattern as the EPD listener)
and reads 24-byte ``struct input_event`` records (type at +0x10,
code at +0x12, value at +0x14), timestamps them, and feeds
TouchConsumer::processTouchEvent → the Java onTouchPointReceived callback.
This is the reader that fills the ION handwriting buffer
(CONFIG_ONYX_EPDC_HANDWRITE_BUF_MALLOC_FROM_ION, The e-ink software stack (Onyx EPDC)).
12.4.3.3. Bitmap pre-processors: dither and RGBW¶
libonyx_neo_dither.so and libonyx_cfa.so are pure JNI bitmap helpers
over libjnigraphics — neither opens /dev or calls ioctl; they
transform pixels before the framework submits the buffer, and are not the
“userspace end of /dev/ebc” the name might suggest.
libonyx_neo_dither.so(log tagneo_color_filter) exportsDitherUtils.dither/DitherUtils.ditherColorand the classesimgfilter::ImageFilter/ImageColorFilter/ImageFilterRGB565, each adoFilterInPlace(AndroidBitmapFormat, ptr, w, h)that dithers the locked bitmap in place (RGBA_8888andRGB_565).libonyx_cfa.so(log tagonyx_cfa) exportsQRCodeUtil.toRgbwBitmapand aColorUtilswithred/green/blue/gray/white+toRed/toGreen/toBlue/toWhite— it packs anRGBA_8888bitmap into the panel’s RGBW sub-pixel layout (the colour filter array for the Kaleido-class panel, The e-ink software stack (Onyx EPDC)).
The two smallest e-ink-adjacent libraries are libonyx_algorithm.so (a single
scribble.utils.Algorithm.distance geometry helper) and libonyx_dsl.so
(an ABBYY-Lingvo DSL dictionary provider — not e-ink at all). Several of these
libraries also embed a shared DeviceUtils::isValid helper that calls back into
Java android/hardware/DeviceController.systemIntegrityCheck()Z — a soft
anti-tamper gate; it is orthogonal to the display path.
12.4.3.4. Deeper static RE: the small helper libs, the pen pressure curve, and libneo_pen.so¶
The subsections above catalogue libonyx_cfa.so / libonyx_neo_dither.so /
libonyx_pen_touch_reader.so by exported JNI class and general shape. This
section goes one level deeper — actual disassembly of the exported functions’
bodies, not just their names — for those three plus the two libraries that
were previously only name/size-catalogued: the 4 KB libonyx_algorithm.so
and the 3.8 MB handwriting engine libneo_pen.so. Full instruction
transcripts, file offsets and the literal constant bytes for every claim
below are in artifacts/re_static/item_onyx_native_deep.txt; only the
findings are summarised here. Confirmed for all five: none of them issues
any ``ioctl`` at all (artifacts/re_static/ioctlscan.py reports 0
callsites in four of the five, and exactly the nine evdev EVIOCG* calls
already known in libonyx_pen_touch_reader.so — never /dev/ebc or
/dev/onyx/*), so the “outside the /dev/ebc path” judgement holds for
every one of these libraries as a file. One of them has a subtler
qualification, noted below.
libonyx_algorithm.so (4104 bytes total) has exactly one export,
Java_com_onyx_android_sdk_scribble_utils_Algorithm_distance, and its
entire 128-byte body is a textbook point-to-line-segment distance:
project the third point onto the line through the first two, clamp the
projection parameter to [0, 1], and return the Euclidean distance to the
clamped point. That is the whole library — a single geometry primitive for
the scribble/stroke code (most likely stroke simplification or hit-testing,
though unconfirmed which one) — fully covered, no device access of any kind.
libonyx_cfa.so’s ColorUtils channel extractors confirm the pixel
format is plain 0xAARRGGBB (red() = >>0x10, green() = >>8,
blue() = identity, alpha() = >>0x18), and ColorUtils::gray()
disassembles to a literal 0.587*G + 0.299*R + 0.114*B — the exact ITU-R
BT.601 luma weights, read directly out of the .so as IEEE-754 doubles at
file offsets 0x22a0/0x22a8/0x22b0. ColorUtils::white(r,g,b) is
the same formula’s fixed-point twin: (299r + 587g + 114b) / 1000 via a
reciprocal-multiplication divide (magic constant 0x10624DD3). The sole
JNI export, QRCodeUtil.toRgbwBitmap, uses exactly this: for every source
ARGB pixel it writes a 2x2 block of four destination pixels — one
red-replicated (argb(a,r,r,r)), one green-replicated, one blue-replicated,
and one carrying the BT.601 luma replicated across all three channels (the
“white”/K tap) — i.e. despite the QR-code-sounding class name, this is the
generic RGB→RGBW colour-filter-array packer for the Kaleido-class colour
panel, with the per-tap formula and luma weights now pinned by bytes rather
than inferred from the function name.
libonyx_neo_dither.so’s grayscale ImageFilter::doFilterInPlace is a
genuine error-diffusion dither, fully disassembled: it computes a
per-pixel weighted grey level (11*R + 16*G + 5*B) / 32 — a different,
independently-authored weighting from the CFA library’s BT.601 constants —
quantizes it to a configurable quant_bits depth (visible in its own log
string, "quant_bits: %d, quant_step: %d"), and forward-diffuses half the
quantization residual into a per-column error-line buffer for the next row.
This is a simplified 2-tap forward/down variant of the Floyd–Steinberg
family, not the textbook 7/16-3/16-5/16-1/16 four-neighbour kernel — no such
coefficients appear anywhere in the disassembly. ImageColorFilter (the
ditherColor export) runs the identical quantize/diffuse math but
allocates three independent error-line buffers, one per R/G/B channel,
confirming colour dithering is per-channel rather than luma-derived.
libonyx_pen_touch_reader.so’s PenManager::normalizePressure() turns
out to implement a real, hand-tuned piecewise pressure-response curve,
not a raw pass-through: it low-pass-filters consecutive raw samples
(smoothed = (0.5*prev + raw) / 1.5), normalises against the device’s
EVIOCGABS(ABS_PRESSURE).maximum, and then applies a 4x linear gain for
the lightest 15% of the range (boosting faint initial pen contact) that hands
off — continuously, verified by evaluating both branches at the 0.15
threshold and getting the same 0.60 — to a compressive power curve
pow(frac-0.135, 0.2)+0.17 for the rest, with all three constants
(-0.135, 0.2, 0.17) read as literal doubles out of the binary.
(There is also an unconditioned fallback branch, gated on an unrelated
per-instance field, that returns the raw sample unmodified — its trigger
condition was not traced further.) Button handling maps the evdev codes
precisely: BTN_TOOL_RUBBER (physical eraser tip), BTN_TOUCH and
BTN_STYLUS (side button) combine in isShortcutErasing() — named field
by field in that function’s own log string — to support a chorded
“hold-side-button-to-erase” shortcut. Region clipping
(inLimitRegion/inValidRegion/inExcludeRegion) expands each
sample point into a tiny stroke-width-sized box and tests it against
app-supplied allow/deny rectangle arrays (setLimitRegion/
setExcludeRegion) — this is what the “coordinate transform” question
resolves to in this library: there is no calibration/rotation matrix
anywhere in it, only region clipping. Likewise moveTo()/quadTo()
turn out to be two-line wrappers that just tag an event-type flag for
report() — no Bézier/quadratic math happens in this library despite the
Android-Path-style naming; if curve smoothing happens anywhere, it is
either in the Java SDK layer or in libneo_pen.so (next).
libneo_pen.so (3.8 MB) exports only seven of its own symbols — the
JNI entry points nativeCreatePen/nativeDestroyPen/nativeOnPenDown/
nativeOnPenMove/nativeOnPenUp/nativeSetBitmapColor/
nativeSetLogLevel of class com.onyx.android.sdk.pen.NeoPenNative —
plus roughly 971 more symbols (978 total defined dynamic symbols) that turn
out to be the public API of statically-linked spdlog and fmt (783 and 45
symbols respectively) and
the same small JNIUtils/JNIBitmap/ColorUtils/DeviceUtils
glue shared with the other onyx libs. The actual handwriting engine is
compiled with hidden visibility, so its own classes carry no dynamic
symbols — but the compiler’s C++ RTTI typeinfo-name strings for those
classes are still present as literal ASCII in the binary (strings -a
finds them even though nm -D cannot), and they lay out the real
architecture precisely: neo::pen::NeoStrokeRenderer, a templated
StrokeProcessor<A,B> chaining TouchEvent → StrokePointBlob →
StrokeSpline, a SplineProducer/SplineInterpolator family
specialised into SplineDistanceInterpolator (arc-length parameterised)
and SplineCurvatureInterpolator (curvature-adaptive recursive
subdivision — its mangled lambda names literally include
subdivideRecursive/subdivideRecursiveXY/getTForCubic, the same
family of technique as AGG-style curve flattening), and a SmoothFilter
(paired with a smoothLevel string). Together with the JNI method names
and log strings drawStroke/"invalid empty stroke renderer!"/"alloc
render points/result data failed!", this confirms libneo_pen.so is the
low-latency ink stroke-rendering engine: it ingests raw pen samples,
curve-fits/smooths them via curvature-adaptive spline subdivision, and
rasterises the result directly into a locked Android Bitmap
(AndroidBitmap_lockPixels is imported; no ioctl/open/mmap is).
Disassembly of the fully-transcribed entry points bears this out structurally
— nativeOnPenDown/nativeOnPenUp marshal a Java touch-point object
through a JNI vtable call into a native record carrying position, at least
two extra float channels, and a 64-bit timestamp, then dispatch to internal
handlers; nativeSetBitmapColor is a NEON-vectorised pass that recolours
every opaque-white pixel of a bitmap to translucent red (0x80FF0000,
literal constant) and clears every other pixel to transparent — a
mark/highlight-region helper, not ink rendering itself.
This library is also where the “outside the boot path” scope judgement needs
a real qualification rather than a blanket reconfirmation. SurfaceFlinger’s
own strings (already recorded above: neopen_createPen, createPen,
"invalid empty stroke renderer!") are byte-for-byte identical to
strings found inside libneo_pen.so — but surfaceflinger’s NEEDED
list (56 entries, checked in full) does not include libneo_pen.so.
The two binaries therefore do not share this code at the dynamic-linking
level; the more likely explanation (unconfirmed — no build system or source
tree was available to check directly) is that the same neo::pen C++
source is compiled into both independently — once as the standalone,
JNI-callable, device-free libneo_pen.so documented here, and once
statically embedded directly inside SurfaceFlinger as its
neopen_createPen/createPen direct compositor-side fast-draw path,
which does go on to call ioctl(/dev/ebc, SET_EBC_SEND_UPDATE, ...) for
the actual panel refresh. So: the libneo_pen.so file never touches a
device — confirmed — but the stroke-rendering algorithm it implements has
a sibling copy that sits squarely in the low-latency ink display path. That
is a more precise finding than either “it’s part of the EPD path” or “it’s
completely unrelated to the EPD path”.
12.4.3.5. The userspace to kernel EPD update path¶
The panel-update ioctls on /dev/ebc are not issued by any libonyx_*
library. Grepping the images, /dev/ebc appears in vendor only in
init.onyx.rc (chmod 0666) and the sepolicy file_contexts (label
ebc_device), and in system only in bin/charger (the minui charge
screen) and bin/surfaceflinger. The real client is the Onyx-patched
SurfaceFlinger, and the app-facing API is compiled into a patched
``libgui.so``: Onyx extended the core AOSP graphics classes with an e-ink
update list that rides the normal buffer-queue/Parcel path. The exported symbols
(defined in libgui.so) are:
symbol (demangled) |
role |
|---|---|
|
a wrapper around |
|
the app attaches its e-ink update regions to a |
|
the same, through a |
|
the list is flattened into the buffer’s Parcel and unflattened in SurfaceFlinger |
So the flow is: an app calls Surface::transferEpdc with a list of
hwc_epdc_llist regions → libgui flattens it into the frame’s Parcel
(writeEpdc) → SurfaceFlinger unflattens (readEpdc), merges same-mode
regions (EpdcWrapper::mergeByMode) and calls its onyx_epdc_screenRefresh,
which issues ioctl(/dev/ebc, SET_EBC_SEND_UPDATE, …). That request number was
pinned by cross-referencing SurfaceFlinger’s own error string
(onyx_epdc_screenRefresh(): error! SET_EBC_SEND_UPDATE retval = 0x%x): the
ioctl immediately preceding it loads w1 = 0x700c, so:
SET_EBC_SEND_UPDATE = ioctl request 0x700c
The other EBC ioctls SurfaceFlinger issues sit in the same 0x70xx family
(e.g. 0x701d), i.e. the /dev/ebc ABI uses plain 0x7000-based magic
numbers (the rockchip-style “ebc” numbering the Onyx software EPDC reuses); their
individual names are the GET_EBC_* / SET_EBC_* set recovered kernel-side
(The /dev/ebc interface: ioctls, update modes, and the waveform format). SurfaceFlinger also carries a
direct pen “stroke renderer” fast-draw path (neopen_createPen / createPen,
### disable refresh, /sys/onyx_misc/cytp_lo_filter) for low-latency ink.
12.4.4. Configuration, policy and the /onyxconfig store¶
Init glue —
init.onyx.sh,init.onyx.misc.sh,init.onyxconfig.sh(system) andinit.onyx.rc(vendor, The e-ink software stack (Onyx EPDC)). These set up device knobs, gatedownload_modeoff on user builds unless a production-test tag file exists, and start the config store.The ``/onyxconfig`` partition — a dedicated ext4 partition (Partition map and checksums, raw image
lun0/onyxconfig.bin) holding anmmkvdirectory: Onyx’s persistent settings/config store, separate from/dataso it survives factory reset. On this unit it holds exactly four top-level entries: the panel VCOM value (com.android.vcom=[REDACTED — unique per physical unit], E-ink driver internals (register-level, from the kernel)) and two 4-byte factory-done markers,com.onyx.android.production.testandcom.onyx.android.screenmanager, both literally the ASCII stringDONE— plustest_result_infobelow.``/onyxconfig/test_result_info`` — a 1 KB JSON record written by the factory production line itself, carried through to the shipped device rather than wiped: model
NoteAir5C, the unit’s WiFi MAC ([REDACTED — unique per physical unit]), and three timestamped test-station passes — SMT ([REDACTED — narrows production timeframe], board-level: charge, WiFi, BT, dual-speaker, dual-mic record, OTG, keys,SENSOR_TEST_TYPE_ACCELEROMETER/_GYROSCOPE, fingerprint, “note” i.e. pen), ASSEMBLE ([REDACTED — narrows production timeframe], anAGING_TESTburn-in pass), and QA ([REDACTED — narrows production timeframe], final line: addsSCREEN_FRONTLIGHT_TEST,SCRIBBLE_TEST,VCOM_SETTING,NATURAL_LIGHT_TEST,ANALOG_EARPHONE_TEST/DIGITAL_EARPHONE_TEST,SDCARD_TEST) — all results1(pass). This independently corroborates, from the factory’s own checklist rather than static code RE, the full sensor/peripheral inventory already recovered in Wacom EMR pen digitizer driver and Misc sensors (fingerprint, hall, keyboard cover) (Wacom EMR pen, fingerprint, IMU) and the front-light/VCOM path in E-ink driver internals (register-level, from the kernel). Thefingerprintfield in this JSON is a build fingerprint string (ONYX/TabBoox/TabBoox:13/TKQ1.230615.001/…), not a biometric fingerprint — most plausibly a stale/default value baked into the factory-test tool itself (unconfirmed; an Android-13 base string on a shipping Android-15 build, see the version split noted at the top of this page); either way, it is not a reliable version record.Permissions / policy —
privapp-permissions-onyx.xml(priv-app grants),onyx-default-permissions.xml(runtime-permission pre-grants),sysconfig/onyx_whitelist.xml(power/hibernation whitelist), andonyx_unavailable_features.xml(the “no NFC/GPS/camera/telephony” declaration, The e-ink software stack (Onyx EPDC), Absent and vestigial hardware (reference-base leftovers)).Onyx properties —
vendor.onyx.tablet=true,vendor.onyx.htcon=true,sys.onyx.idledelay=2500,ro.build.version.onyxincremental=225/ro.vendor.build.onyxid=…(the Onyx build-tracking IDs).
12.4.5. Preinstalled Onyx apps¶
The Boox application suite itself — the note app, reader, app store, cloud
sync, and the rest — is catalogued in Preinstalled apps (Boox, GMS, AOSP); an onyx-only
search for them misses their naming convention, but does surface
OnyxLatinIME (keyboard) and OnyxOtaService (the OTA service, with
libota_jni.so, driving the A/B updater in Userspace boot — init, SELinux, A/B updates). This
page covers the platform scaffolding those apps run on (the SDK libraries,
the EPD/pen native path, the config store, the permissions); the apps’ own
compiled Java is not decompiled.
12.4.6. Scope boundary¶
The compiled Java of the TabBoox framework, the two Onyx system services, and
the Boox app suite itself (odex/vdex, all preinstalled per Preinstalled apps (Boox, GMS, AOSP))
is not decompiled here; nor is the product partition’s Google/AOSP payload,
covered separately in Preinstalled apps (Boox, GMS, AOSP). This page is the map of the Onyx
platform scaffolding, not a reverse of the Boox applications.
12.4.7. Provenance¶
- Source:
artifacts/notes/props/*.build.prop(the split fingerprints, Onyx properties),system_a.img(lib64/libonyx_*.so,lib64/libmmkv-onyx.so,bin/init.onyx*.sh,etc/permissions/sysconfigOnyx XML,app/OnyxLatinIME,priv-app/OnyxOtaService,framework/*.jar), partition listings for the app census. The “Inside the native Onyx SDK” section additionally uses the extracted.sobinaries themselves —lib64/libonyx_epd_listener.so,libonyx_pen_touch_reader.so,libonyx_cfa.so,libonyx_neo_dither.so,libgui.soandbin/surfaceflinger— with the full symbol dumps, ioctl/dev/property strings and disassembly recorded inartifacts/re_static/item4_onyx_native.txt. The “Deeper static RE” subsection adds a second pass overlibonyx_algorithm.so,libonyx_cfa.so,libonyx_neo_dither.so,libonyx_pen_touch_reader.soandlibneo_pen.so— full instruction transcripts, extracted floating-point/fixed-point constants, and theioctlscan.pyper-library ioctl-callsite counts are inartifacts/re_static/item_onyx_native_deep.txt.- Method:
ext4 images listed/extracted read-only with
7z; properties and init scripts read as text. Dynamic symbols read withobjdump -T/nm -Dand demangled withc++filt; exported functions disassembled with capstone (helper scriptsartifacts/re_static/sodis.py/ioctlscan.py); ioctl request numbers decoded from themovz/movkimmediates and pinned to names by error-string cross-reference. Compiled Java not decompiled. No code executed.- Cross-refs:
The e-ink software stack (Onyx EPDC) (EPD/pen native libraries), E-ink driver internals (register-level, from the kernel) (kernel-side
/dev/ebcioctl set), Security and DRM userspace (keystore2-on-keymaster4.1, explained by the 13-on-11 split), Userspace boot — init, SELinux, A/B updates (A/B + OTA), Preinstalled apps (Boox, GMS, AOSP) (GMS/AOSP apps), Partition map and checksums (theonyxconfigpartition), Absent and vestigial hardware (reference-base leftovers) (unavailable features).