Aller au contenu
Tous les articles

La migration Android vers les pages mémoire de 16 Ko : ce qui casse vraiment et comment vérifier ton APK

Google exige désormais que les apps Android prennent en charge les pages mémoire de 16 Ko. Voici ce que ça change en pratique, pourquoi les bibliothèques natives sont celles qui cassent, et comment vérifier que la tienne est sûre.

MFKAPPS 6 min de lecture

Si ton app embarque du code natif — ton propre JNI, ou simplement un SDK tiers qui inclut un fichier .so — il y a de bonnes chances qu’elle transporte une mine qui n’explose que sur du matériel récent. Android prend désormais en charge les pages mémoire de 16 Ko en plus des pages traditionnelles de 4 Ko, et Google Play exige que les apps ciblant les versions récentes d’Android fonctionnent correctement avec les deux. La plupart des apps 100% Kotlin traversent ça sans le moindre changement. Les apps avec des dépendances natives, souvent pas — et le mode d’échec est franchement désagréable : pas un avertissement de lint, mais un crash à l’installation ou au premier lancement, uniquement sur les appareils qui utilisent réellement des pages de 16 Ko.

J’ai fait cette migration sur toute la suite d’apps — la quasi-totalité s’est passée sans encombre, sauf une dépendance. Voici en quoi consiste vraiment la vérification.

Pourquoi la taille des pages compte

Une page mémoire est la plus petite unité que l’OS utilise pour gérer la mémoire. Android utilise des pages de 4 Ko depuis le début ; certaines puces récentes utilisent désormais des pages de 16 Ko par défaut, car des pages plus grandes réduisent la charge de gestion mémoire pour les tâches gourmandes en RAM — moins d’entrées de table des pages, moins de défauts TLB, des lancements d’app en moyenne plus rapides. L’OS et l’Android Runtime gèrent ça de façon transparente pour le code Kotlin et Java ordinaire. C’est invisible pour ViewModel, Room, Compose — rien de tout ça ne se soucie de la taille de page sous-jacente.

Le code natif, lui, s’en soucie, parce que les bibliothèques natives sont des binaires ELF, et les binaires ELF ont leurs segments chargeables alignés sur une limite fixe au moment de la compilation. Un .so compilé avec des segments alignés sur 4 Ko fonctionne très bien sur un appareil à pages de 4 Ko. Charge ce même fichier sur un appareil qui tourne avec des pages de 16 Ko, et l’éditeur de liens dynamique peut échouer à le mapper correctement — l’app ne démarre pas, ou plante dès qu’elle touche cette bibliothèque.

Ce qui casse vraiment

Pas ton code Kotlin. La liste du risque réel est courte et précise :

  • Tes propres modules NDK/JNI, si tu en compiles.
  • Les AAR tiers qui embarquent des bibliothèques natives précompilées — traitement d’image, rapport de crash, moteurs de base de données, runtimes de ML. Tout ce qui a un .so dans son AAR est un candidat.
  • Une sortie d’ancienne chaîne d’outils. Une bibliothèque compilée avec un NDK plus ancien, avant que la chaîne d’outils n’aligne par défaut les segments sur 16 Ko, est le cas d’échec le plus courant, même dans un SDK par ailleurs activement maintenu — le binaire n’a simplement pas été recompilé depuis que l’alignement par défaut a changé.

Ce qui ne casse pas : les dépendances 100% Kotlin, tout ce qui est livré uniquement en .jar ou .aar sans code natif, et les bibliothèques propriétaires de Google dans leurs versions récentes, déjà recompilées avec le bon alignement.

Vérifier ce qu’il y a vraiment dans ton APK

Avant de toucher à la configuration de build, vérifie si tu es réellement exposé :

unzip -l app-release.apk | grep '\.so$'

Si ça ne renvoie rien, c’est terminé — il n’y a pas de code natif à aligner. Si ça renvoie des entrées, vérifie directement l’alignement des segments de chaque bibliothèque :

unzip -p app-release.apk lib/arm64-v8a/libsomething.so | \
  readelf -l - | grep LOAD

Regarde la colonne Align de chaque segment LOAD. 0x4000 (16 Ko) signifie que cette bibliothèque est déjà correctement alignée. 0x1000 (4 Ko) signifie que ce n’est pas le cas, et c’est celle-là qu’il faut traquer — soit une version mise à jour du SDK dont elle vient, soit une recompilation si c’est ton propre code.

L’APK Analyzer d’Android Studio affiche la même information sans passer par la ligne de commande : ouvre l’APK, descends jusqu’à un fichier .so sous lib/, et il signale directement un alignement limité à 4 Ko.

La correction, par ordre de contrôle que tu as dessus

  1. Ton propre code natif : recompile avec un NDK récent. Les versions récentes de la chaîne d’outils produisent par défaut une sortie alignée sur 16 Ko, donc c’est souvent juste une montée de version dans build.gradle.kts plus une recompilation propre — aucun changement de code source.
  2. Un SDK tiers avec un vieux .so : mets à jour la dépendance. Presque tous les SDK activement maintenus ont publié une version alignée sur 16 Ko ; la correction est une montée de version, pas un contournement.
  3. Une dépendance qui n’a pas été mise à jour : c’est le cas inconfortable. Tes vraies options sont un ticket ouvert contre le SDK, un fork et une recompilation si c’est open source, ou l’abandon de la dépendance. Il n’existe aucune astuce au niveau de l’APK qui réaligne un .so que tu n’as pas compilé toi-même.

La bibliothèque qui m’a piégé était exactement dans le cas 2 — une extension de base de données livrant des binaires natifs précompilés pour le chiffrement au repos derrière l’historique des transactions de Granyn. Une simple montée de version mineure a apporté la version alignée ; rien n’a changé dans le code d’intégration.

Tester sur un vrai appareil en 16 Ko

Les vérifications d’alignement te disent ce qui devrait se passer ; tester sur de vraies pages de 16 Ko te dit ce qui se passe réellement. L’émulateur Android propose des images système construites spécifiquement pour ça — cherche une variante « 16 KB Page Size » d’un niveau d’API récent dans le SDK Manager, crée un AVD à partir de celle-ci, et installe ton APK exactement comme tu le ferais pour une sortie en production. Si quelque chose qui utilise une bibliothèque mal alignée doit planter, ça plantera là, immédiatement et de façon reproductible — un bien meilleur endroit pour le découvrir qu’un ticket de support venant d’un appareil que tu ne possèdes pas.

À retenir

Pour la plupart des apps orientées Kotlin, cette migration est invisible — pas de code natif, pas d’exposition, rien à faire. Pour tout ce qui tire des dépendances natives, la correction est presque toujours une montée de version de dépendance plutôt qu’une réécriture : trouve le .so mal aligné avec readelf, mets à jour ce qui l’a livré, recompile, et vérifie sur une image système 16 Ko avant de publier. La vérification prend dix minutes. La sauter, c’est le découvrir plus tard via un rapport de crash.