SENARYSTUDIOS

Notes · Senary Launcher 1.5

Sixteen kilobytes

The first native code in Senary Launcher, and what it took to reach Google Play: 16 KB memory pages, a processor picked at run time, a compiler crash and a 76 MB mistake. A debug build showed one of them.

Until 1.5, Senary Launcher was all Java and Kotlin, which Android runs on any phone as it is. Voice changed that. Senary's own speech recognition runs Whisper through whisper.cpp, which is C and C++, compiled ahead of time into libraries for the phone's processor. Native code comes with rules the rest of an app never meets, and each of the four below turned up on 1.5's way to Google Play.

Sixteen kilobytes

A phone hands memory to apps in pages. For most of Android's life those pages have been 4 KB; from Android 15, a phone can use 16 KB pages instead, which Google says makes apps start faster when memory is short. A native library has to be laid out to match, with each part the phone loads starting on a 16 KB boundary. One built for 4 KB pages runs on a 16 KB phone, if at all, only in a compatibility mode. Google Play asks apps that target Android 15 or later to support 16 KB pages, and from 1 February 2027 it will not accept updates that do not.

The first build with whisper.cpp in it used the version of Android's native toolchain, the NDK, that the build tools installed by default: r27. Installed on the 10a, it said so straight away: “This app isn't 16 KB compatible. ELF alignment check failed.”

Senary now pins the NDK to r30. Google's documentation says r28 and later align to 16 KB by default, and Senary also passes the two linker settings that force it, so the alignment never rests on a default. The warning stopped. It is not the real check, though, because only a debug build on a recent Android shows it. The real one is reading the libraries back: all 37 in the release bundle, every part the phone loads aligned to 16 KB.

One library per processor

whisper.cpp is fast partly because it uses instructions that only newer Arm processors have. A library built for the newest cores would crash on older ones, and one built for the oldest would leave the newer ones' instructions unused. So its processor code is built several times, once per generation, and the right one is chosen on the phone, at run time, by looking in the folder where the app's libraries live.

That folder only has anything in it if the libraries are unpacked when the app is installed, rather than read straight out of the app's package. So Senary unpacks them. It is the older of Android's two ways to ship libraries, and here the older way is the one that works.

A compiler that crashed

Debug builds compiled cleanly. The first release build did not: the compiler itself crashed, with an internal error, on one of ggml's files for the newest processor generation. A release build's native code defaults to a different set of optimisations, with debugging information included, and under those settings it crashed. Plain Release settings compile, and they are the ones every measurement on the phone was taken with, so release builds use them too.

76 MB for 44

The speech model does not ship with the app. It is a separate pack that Google Play downloads the first time it is needed. The pack takes everything in one folder, and an older version of the model had been left in that folder after a switch, so the first release bundle carried both: 76 MB for someone who needs 44. The build now clears out anything in that folder that is not the one model it expects, and refuses that model when it is fetched unless its fingerprint matches.

What a debug build does not show

Of these four, only the 16 KB warning ever appeared in the builds used day to day. So a release bundle gets its own checks before it is uploaded: one model in the pack, every library aligned, the code that calls into the native libraries still named the way they expect after the release build shrinks it, and the licence check passing. Google Play's review page then counts the devices an update would leave behind. 1.5 left none.

One limit is by design. Whisper is built only for 64-bit phones, so on a phone with neither its own speech recognition nor a 64-bit processor, holding the search bar says voice is not available.

More notes: all of them. Why there is native code at all: Voice without a recogniser.