android_4k_issue

Google Play and 16KB Page Size: How to Check APK/AAB Before Uploading and Find Broken Native

In 2025, Google Play started gradually enforcing new requirements for apps targeting Android 15+ (targetSdk 35). One of the most painful ones is compatibility with devices using 16 KB memory pages (16KB page size).

In practice, it often looks like this:

  • the app builds fine,
  • it works locally,
  • but when you try to publish an update in Google Play, you get an error saying your app does not support 16KB page size,
  • and the root cause is… native .so libraries inside your APK/AAB.

This post shows how to quickly check your build before uploading to Play, locate the problematic .so files, and automate the check in Gradle.


What is 16KB page size and why does it break publishing?

Historically, most Android devices used 4 KB memory pages. However, some newer devices (especially on Android 15 and later) use 16 KB.

If your app includes native code (NDK, game engines, SDKs, ML, games, etc.), your APK/AAB will contain .so files.

And here is the key point:

Compatibility is not determined by “zipalign”, not by Gradle settings, and not by targetSdk itself — it is determined by how your ELF libraries were built.

.so files contain a segment alignment field (LOAD Align) that you can inspect using readelf.

If a library is built with Align = 0x1000 (4 KB), it may be incompatible with 16 KB page size and Google Play may block publishing.


Which ABIs actually matter?

The 16 KB page size requirement is primarily relevant for 64-bit ABIs:

  • arm64-v8a
  • x86_64

32-bit ABIs (armeabi-v7a, x86) are usually not critical (real devices on those ABIs almost always use 4 KB pages). However, depending on the exact Play Console validation, warnings may still appear for 32-bit libraries.

In my own checker I ended up validating only 64-bit ABIs — that matches the real-world problem.


Why did it publish yesterday but fails today?

A very common scenario:

  • your previous release is already published,
  • you make a small bug fix,
  • build an update,
  • and suddenly Google Play starts complaining.

This happens because:

  • Play gradually tightens validations,
  • or you updated targetSdk / AGP,
  • or your dependencies changed,
  • or the new build simply pulled different .so files.

This is especially common with:

  • game engines (libGDX, Unity, Godot),
  • ML/vision SDKs,
  • ad SDKs,
  • “ready-made” AAR/JARs that embed .so files.

How to find which .so files are actually problematic

You need one of these tools:

Option A: readelf (Linux/macOS)

On Linux it is usually available out of the box. On macOS you can install it via brew.

Option B (universal): llvm-readelf from Android NDK

This is the most convenient option because:

  • it works on Windows,
  • it is stable,
  • it is always available if Android NDK is installed.

What is considered a “valid” Align?

Google talks about compatibility with 16 KB.

That means valid alignment values are:

  • 0x4000 (16 KB) ✅
  • 0x8000 (32 KB) ✅
  • 0x10000 (64 KB) ✅

So the rule is simple:

Align must be at least 0x4000 and a multiple of 0x4000.

And values like:

  • 0x1000 (4 KB) ❌
  • 0x2000 (8 KB) ❌

— are a real problem.


Quick APK/AAB check (Python utility)

I wrote a small script that:

  1. unpacks an APK or AAB
  2. finds all .so files
  3. checks LOAD Align via llvm-readelf
  4. prints a list of problematic libraries
  5. exits with an error (so it can be used in CI)

Download the script

👉 Download link:
app_checker.py


Example usage

Example output when something is wrong:


What to do if you find 0x1000

If the issue is in your own .so (you build native code yourself), the usual fix is:

  • update Android NDK to a recent version,
  • rebuild your .so.

If the issue is in a third-party library, your options are:

  • update the SDK/engine to a version where the .so files are already rebuilt for 16KB,
  • or replace the library,
  • or (in rare cases) build the natives yourself.

Automatic Gradle check (before uploading to Google Play)

Manual checks are useful, but the best approach is to automate everything so the build fails immediately if incompatible .so files end up in your APK/AAB.

I added a Gradle task called check16kPageSize that:

  • finds release APK/AAB artifacts,
  • unpacks them,
  • checks .so via llvm-readelf from Android NDK,
  • checks only arm64-v8a and x86_64,
  • fails with a clear list of problematic files.

Requirements

To run the Gradle task you need:

  • Android NDK installed
  • and Gradle must be able to locate llvm-readelf

Usually NDK is installed via Android Studio:

Tools → SDK Manager → SDK Tools → NDK (Side by side)


Gradle task check16kPageSize

Paste this code into android/build.gradle (or into the module that builds your APK/AAB).


How to run it

After building a release:

or

the check will run automatically.

You can also run it manually:


Why the problem is usually in third-party SDKs

The most annoying part:

  • you can update Gradle, AGP, targetSdk
  • but if your APK contains a third-party .so built with Align = 0x1000, nothing will change.

This is why the 16KB issue often hits:

  • libGDX (especially libgdx.so and libgdx-freetype.so)
  • older TensorFlow Lite / ML Kit versions
  • ad SDKs
  • any AAR/JAR with embedded natives

Summary

If your app contains .so files, it is worth doing this right now:

  • ✅ run a local APK/AAB check
  • ✅ add the check to Gradle/CI
  • ✅ catch broken libraries before uploading to Play

Because once Play Console starts rejecting your upload, you are usually already close to a deadline — and changing SDKs at the last minute is painful.


Useful links

  • Android documentation about page sizes:
    https://developer.android.com/guide/practices/page-sizes
  • Android Developers Blog about the 16 KB transition:
    https://android-developers.googleblog.com/

If this post helped you — feel free to leave a comment with which SDKs/engines you hit the 16KB issue with. I’ll keep a list of common offenders and solutions.

Read More

Quickly view seamless textures online

When working with AI-generated images, one common issue is how to quickly check whether a texture is truly seamless (tileable). This is especially useful if you generate textures using Stable Diffusion, create patterns with ChatGPT, or use any other AI tools for texture generation.
In practice, it turns out that there aren’t many simple and convenient utilities for seamless texture testing, especially ones that work directly in the browser without installing anything.
That’s why I created a small HTML-based online tool that solves this problem.
Online page with the tool
👉 Seamless Texture Viewer
(more…)

Read More

Install LTIB BSP on Ubuntu 14.04

The LTIB (Linux Target Image Builder) project is a simple tool that can be used to develop and deploy BSPs (Board Support Packages) for various target platforms. Using this tool a user will be able to develop a GNU/Linux image for their target platform.
This instruction helps to build and install LTIB for conga-QMX6 (based on Freescale ® i.MX6 ARM ® Cortex A9) on Ubuntu 14.04 Linux. (more…)

Read More

IMG_9084

Repairing of monitor Philips 170B4

Yesterday I bought Philips 170B4 LCD 17′ monitor on the sale.
It was very cheap (about 9$), and of course when I turned it on at home, it didn’t work. My OS detected it right, but the screen was total black. Monitor menu worked fine, and showed Brightness and Contrast level equal -1 (minus one). My attempts to restore factory settings via menu made no results. I digged in the Internet and found some reference to “incorrect firmware in that case”. Learned out Service Manual that monitor had EEPROM for saving settings, and got consciousness – in my case data stored in EEPROM was broken.
So to solve this problem you should follow this way: (more…)

Read More