Introduction
Dans les précédents articles, nous avons vu comment produire une image AOSP utilisable dans l'émulateur Android. Nous avons également décrit les principales fonctions de l'outil ADB (Android Debug Bridge) indispensable au développement Android « système ».
Dans cet article nous allons décrire la procédure de mise à jour du noyau Linux utilisé par l'émulateur Android. En effet, le noyau utilisé jusqu'à présent n'a pas été compilé car il est fourni sous forme binaire par AOSP dans le répertoire prebuilts.
$ ls -l prebuilts/qemu-kernel/arm/
total 17736
drwxr-xr-x 2 pierre pierre 4096 Oct 16 11:33 2.6
-rw-r--r-- 1 pierre pierre 15872 Oct 16 11:33 LINUX_KERNEL_COPYING
-rw-r--r-- 1 pierre pierre 210 Oct 16 11:33 README
-rwxr-xr-x 1 pierre pierre 2231328 Oct 16 11:33 kernel-qemu
-rwxr-xr-x 1 pierre pierre 2372056 Oct 16 11:33 kernel-qemu-armv7
-rwxr-xr-x 1 pierre pierre 7705 Oct 16 11:33 rebuild.sh
-rwxr-xr-x 1 pierre pierre 5913492 Oct 16 11:33 vmlinux-qemu
-rwxr-xr-x 1 pierre pierre 7602368 Oct 16 11:33 vmlinux-qemu-armv7
Ce noyau a un certain nombre de limitations, entre-autres de ne pas disposer du support des modules dynamiques.
$ adb shell
root@generic:/ # lsmod
/proc/modules: No such file or directory
Ce point peut devenir un problème si l'on veut ajouter des pilotes à la plate-forme dans le cas d'une cible réelle, ce qui arrive fréquemment dans le cas d'applications industrielles. Nous utiliserons l'émulateur mais la procédure est strictement identique pour une cible réelle.
Compilation du noyau Linux pour Android
Les sources du noyau ne sont pas fournies avec AOSP et il est nécessaire de les obtenir auprès d'un dépôt Git hébergé par Google. Pour cela on utilise la commande :
$ git clone https://android.googlesource.com/kernel/goldfish.git
A l'issue du téléchargement, on peut visualiser les différentes branches du dépôt par :
$ cd goldfish
$ git branch -r
origin/HEAD -> origin/master
origin/android-goldfish-2.6.29
origin/android-goldfish-3.10
origin/android-goldfish-3.4
origin/linux-goldfish-3.0-wip
origin/master
Nous allons nous intéresser à la version 3.4 et nous créons donc une branche locale dans ce but.
$ git checkout -t origin/android-goldfish-3.4 -b 3.4
A l'issue de cette commande, nous obtenons l'arborescence des sources du noyau Linux pour la cible goldfish.
$ ls -l
total 504
drwxrwxr-x 29 pierre pierre 4096 19 févr. 14:26 arch
drwxrwxr-x 3 pierre pierre 4096 19 févr. 14:26 block
-rw-rw-r-- 1 pierre pierre 18693 19 févr. 14:25 COPYING
-rw-rw-r-- 1 pierre pierre 94984 19 févr. 14:25 CREDITS
drwxrwxr-x 3 pierre pierre 4096 19 févr. 14:26 crypto
…
La procédure de compilation « croisée » du noyau est identique à celle utilisée dans le cas de GNU/Linux. Le compilateur croisée est fourni dans AOSP. Une fois de plus, il est donc nécessaire de charger les variables d'environnement utilisées par AOSP.
$ cd work
$ source build/envsetup.sh
$ lunch 1
$ type arm-eabi-gcc
arm-eabi-gcc is /home/pierre/Android/work_google/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7/bin/arm-eabi-gcc
Lorsque nous revenons dans le répertoire des sources du noyau, if faut positionner un certain nombre de variables d'environnement, soit :
$ export ARCH=arm
$ export SUBARCH=arm
$ export CROSS_COMPILE=arm-eabi-
La prochaine étape consiste à choisir un fichier de configuration pour le noyau à compiler. Dans notre cas nous utilisons une plate-forme goldfish / ARMv7 d’où la commande :
$ make goldfish_armv7_defconfig
La compilation d'un tel noyau produira une version strictement identique à celle actuellement utilisée. Notre but est de modifier légèrement cette configuration, en particulier de disposer du support des modules dynamiques. Pour cela nous éditons la configuration du noyau par la commande :
$ make menuconfig
Ce qui conduit à l'affichage de l'écran suivant :
Figure 1. Configuration du noyau
Nous constatons que la configuration courante ne dispose effectivement pas du support des modules car l'option Enable loadable module support n'est pas activée. Pour l'activer, il suffit de cocher l'option correspondante ainsi que toutes les options du sous-menu associé :
Figure 2. Activation du support des modules
On peut alors compiler le noyau par la commande make. L'image zImage est disponible dans le répertoire arch/arm/boot. Un nouveau test avec l'émulateur est réalisable par la commande :
$ emulator -kernel arch/arm/boot/zImage &
Une session ADB permet de vérifier la nouvelle version du noyau ainsi que le bon fonctionnement de la commande lsmod.
$ adb shell
root@generic:/ # cat /proc/version
Linux version 3.4.0 (pierre@XPS13-pf) (gcc version 4.7 (GCC) ) #2 PREEMPT Thu Feb 13 18:07:02 CET 2014
root@generic:/ # lsmod
Développement et test d'un module noyau
Nous pouvons à présent développer un module de test « Hello World » que nous chargerons dans l'émulateur Android utilisant ce nouveau noyau. L'API de développement noyau d'Android est identique à celle de GNU/Linux et nous pouvons donc utiliser les même principes que nous allons rappeler brièvement.
Un module noyau est constitué de deux fonctions principales nommées module_init() et module_exit(). La première est appelée lors du chargement du module dans la mémoire du noyau (par insmod ou modprobe) et la seconde appelée lors du dé-chargement de ce même module (par rmmod).
Nous pouvons donc en déduire le code source d'un module très simple basé sur la fonction printk(), équivalente à printf() mais en espace noyau.
#include <linux/module.h>
#include <linux/kernel.h>
MODULE_LICENSE("GPL");
static int __init hello_init(void)
{
printk (KERN_INFO "Hello World\n");
return 0;
}
static void __exit hello_exit(void)
{
printk (KERN_INFO "Goodbye, cruel world!\n");
}
module_init(hello_init);
module_exit(hello_exit);
La compilation du module utilise un fichier Makefile spécial que nous décrivons ci-après. Le fichier indique que la compilation du module nécessite la présence des sources du noyau.
KDIR= $(HOME)/Android/goldfish_kernel
PWD= $(shell pwd)
obj-m := helloworld.o
all:
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
install:
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules_install
clean:
rm -f *~ Module.markers Modules.symvers
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) clean
Une fois les variables ARCH et CROSS_COMPILE renseignées, on peut compiler le module par la commande make, ce qui produit un fichier helloworld.ko. Ce module peut être copié sur la cible en utilisant ADB :
$ adb push helloworld.ko /data
On peut ensuite insérer le module par insmod puis vérifier les traces d'affichage en utilisant la commande dmesg.
$ adb shell
root@generic:/ # insmod /data/helloworld.ko
root@generic:/ # lsmod
helloworld 585 0 - Live 0x00000000 (O)
root@generic:/ # dmesg
...
<6>Hello World
On peut également retirer le module de la mémoire par rmmod.
root@generic:/ # rmmod helloworld
root@generic:/ # dmesg
<6>Goodbye, cruel world!
Conclusion
Nous avons pu réaliser assez rapidement une personnalisation de l'image AOSP en modifiant le noyau utilisé. Malgré sa simplicité, le module compilé et testé constitue les prémisses d'une solution d'ajout de fonctionnalité « matérielle » à un système Android. Nous verrons ce point dans une prochaine publication en étudiant la HAL (Hardware Abstraction Layer) Android et l'utilisation de JNI (Java Native Interface) dans une application Java.
Bibliographie