SENARYSTUDIOS

Notes · Senary Launcher 1.5

Sixteen kilobytes

The launcher's first native code and what it needed for Google Play: 16 KB pages, per-processor libraries, a compiler crash and a 76 MB mistake.

Until 1.5, the launcher was all Java and Kotlin, which runs on any Android 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 for the phone's processor. Native code comes with its own rules, and four of them came up on the way to Google Play.

Sixteen kilobytes

Phones give apps memory in pages. On Android those have mostly been 4 KB; from Android 15, phones can use 16 KB pages, which Google says helps apps start faster when memory is tight. Native libraries have to match, with each part the phone loads starting on a 16 KB boundary. A library built for 4 KB pages runs on a 16 KB phone only in a compatibility mode, if at all. Google Play requires 16 KB support for apps targeting Android 15 or later, and from 1 February 2027 it won't accept updates without it.

The first build with whisper.cpp used the NDK, Android's native toolchain, that the build tools installed by default: r27. 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 says r28 and later align to 16 KB by default, and Senary also sets the two linker flags that force it, so it doesn't rely on a default. The warning went away. That warning isn't the real check, though: only a debug build on recent Android shows it. The real check is reading the libraries back. All 37 in the release bundle are aligned.

One library per processor

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

That folder is only filled if the libraries are unpacked at install, not read straight from the app's package. So Senary unpacks them. It's the older of Android's two ways to ship libraries, and here it's the one that works.

A compiler that crashed

Debug builds compiled fine. The first release build didn't: the compiler itself crashed, with an internal error, on one of ggml's files for the newest processors. Release builds default to different optimisation settings, with debug info included, and under those it crashed. Plain Release settings compile, and they're what every measurement on the phone used, so release builds use them now.

76 MB for 44

The speech model doesn't ship with the app. It's a separate pack Google Play downloads the first time it's needed. The pack takes everything in one folder, and an old version of the model had been left there after a switch, so the first release bundle had both: 76 MB when you need 44. The build now clears out anything in that folder except the one model it expects, and rejects that model at download unless its fingerprint matches.

What a debug build doesn't show

Of the four, only the 16 KB warning ever showed up in day-to-day builds. So release bundles get their own checks before upload: one model in the pack, every library aligned, the native calls still named correctly after the release build shrinks the code, and the licence check passing. Then Google Play's review page counts the devices an update would drop. 1.5 dropped none.

One limit is on purpose. Whisper is only built for 64-bit phones, so on a phone with no speech recognition of its own and no 64-bit processor, holding the search bar says voice isn't available.

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