Compiler votre propre Android pour vos applications embarquées
Android est partout, dans vos téléphones et tablettes, mais depuis plusieurs années déjà, ce petit robot a dépassé le cadre privé des programmeurs, et on le retrouve maintenant dans le monde de tous les jours.
Ainsi il n’est pas rare d’en découvrir dans les taxis, les hôpitaux, les terminaux de paiement, les distributeurs de boissons et j’en passe. Nous allons donc voir, ensemble les étapes clefs pour réaliser son propre système Android-based.
1. Choisir une carte
À défaut de faire son circuit imprimé six couches soi-même, l’idée est de choisir une carte qui correspond le mieux au besoin. Les questions à se poser sont :
- Pérennité du produit ?
- Y a-t-il un service de support ?
- Un code source Android dédié à la carte est-il disponible ?
- De quels périphériques ai-je besoin : GPIO, SPI, I2C, UART, USB, ETHERNET ?
- Écran tactile ou non, résistif ou capacitif ?
- Capacité de stockage, performance CPU ?
- Taille mécanique ?
- Quel prix ?
Notez que, dépendamment des périphériques identifiés, il est fort possible que vous n’ailliez besoin, ni de carte, ni de compiler votre propre Android !
Exemples :
- Vous avez besoin d’un GPS et d’une connexion 4G ? Peut-être qu’une tablette ferait l’affaire.
- Vous avez besoin d’une sortie HDMI et d’une connexion wifi ? Un Android TV Stick est probablement plus adapté.
- Vous avez besoin de GPIO, de commandes moteur et de pilote matériel spécifiques ? Alors là, oui, une carte sera probablement le bon choix.
À titre informatif, voici quelques distributeurs avec lesquels nous avons eu l’occasion de travailler :
- Olimex : http://www.olimex.com/
- Forlink : http://www.forlinx.net/
- Raspberry : http://www.raspberrypi.org/
- ST : http://www.st.com/
- Rockchip : http://www.rock-chips.com/
- BeagleBoard : http://beagleboard.org/
On peut noter qu’en terme de prix, il n’est pas rare de trouver pour une centaine de dollars des cartes équipées d’un écran résistif (technologie souvent utilisée pour les kiosques en raison de sa robustesse). Mais là encore attention ! Toutes ne se valent pas en terme de performance, et l’expérience utilisateur pourrait s’en trouver dégradée.
À titre d’exemple, si on considère la nagivation dans les menus Android2.2, un CPU Samsung S3C6410 est un peu limite, alors qu’un CPU TI Sitara donnera un résultat très acceptable.
2. Le code source Android
Android est un système au code source ouvert. Le lien suivant donne la méthode pour choisir, récupérer et compiler un repository Android : http://source.android.com/source/downloading.html
Mais ne nous y trompons pas, même si c’est très généreux de la part de Google, la version fournie (dite AOSP) est prévue pour les produits... Google !
En standard, on trouvera donc les cibles suivantes :
- ARM emulator
- Nexus based
- PandaBoard
On pourra choisir parmi les noyaux (kernels) suivants :
# starting point for work on Intel x86_64 chipsets. $ git clone https://android.googlesource.com/kernel/x86_64.git # starting point for work on TI OMAP chipsets. $ git clone https://android.googlesource.com/kernel/omap.git # starting point for NVIDIA Tegra chipsets. $ git clone https://android.googlesource.com/kernel/tegra.git # starting point for Samsung Exynos chipsets. $ git clone https://android.googlesource.com/kernel/exynos.git # starting point for work on Qualcomm MSM chipsets. $ git clone https://android.googlesource.com/kernel/msm.git # starting point for Samsung Hummingbird chipsets. $ git clone https://android.googlesource.com/kernel/samsung.git
En résumé, là encore, le choix de la carte est crucial. Et, si vous ne disposez pas d’emblée d’un code source adapté, vous risquez des échanges de mails incessants entre vous et votre fournisseur pour réaliser le travail. (voir le fournisseur du fournisseur...)
Dans la suite de cet article, je prendrai l’exemple de la carte ok335d de Forlinx livrée avec un Android4.2.
Cependant, la majorité des informations qui suivront restent vraies (à quelques ajustements près) pour d’autres configurations.
3. L’Environnement
- L’environnement qui nous a apporté le plus de satisfaction est : Ubuntu 12.04 32bits
- 80 GB de disque dur seront nécessaires
- Un environnement virtuel (VirtualBox) est possible, mais prévoyez environ cinq heures pour la première compilation sur un Core i7-2600 @3.40 GHz
Vient ensuite une liste de packages à installer :
sudo apt-get update sudo apt-get install openjdk-7-jdk sudo apt-get install bison g++-multilib git gperf libxml2-utils make zlib1g-dev:i386 zip sudo apt-get install git gnupg flex bison gperf build-essential \ zip curl libc6-dev libncurses5-dev:i386 x11proto-core-dev \ libx11-dev:i386 libreadline6-dev:i386 libgl1-mesa-glx:i386 \ libgl1-mesa-dev g++-multilib mingw32 tofrodos \ python-markdown libxml2-utils xsltproc zlib1g-dev:i386
Assurez-vous de disposer de la bonne tool-chain pour une cross-compilation vers le CPU de votre carte. Il vous faudra probablement la rajouter à votre PATH.
Exemple :
export PATH=$PWD/prebuilts/gcc/linux‐x86/arm/arm‐eabi‐4.6/bin:$PATH
Tool chain ARM, disponible ici :
$ git clone https://android.googlesource.com/platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.6
4. Compilation du chargeur (u-boot)
C’est le premier élément de la chaîne. C’est-à-dire qu’après le ROM boot (non modifiable par définition), u-boot est le premier binaire exécuté (quelques centaines de kB) au démarrage du système.
Peut-être l’occasion d’y glisser votre premier "printf("Hello world");" ?
Il s’agit surtout de l’élément responsable d’initialiser certains périphériques importants (tels que les IRQ, la mémoire...) afin de charger dans une deuxième étape le noyau Linux. Souvent dans un répertoire séparé, là encore il est sensé être donné par le fournisseur de la carte.
cd u-boot make ARCH=arm CROSS_COMPILE=arm-eabi- -j8
5. Compilation du noyau (uImage)
Le noyau est une branche du Linux 2.6 puis 3.0 depuis Android 4.
cd kernel
C’est ici que l’on trouvera les pilotes pour notre configuration matérielle.
make menuconfig
Mais avant de modifier le ".config" en vous baladant dans ces menus, prenez soin de charger au préalable la configuration de votre carte :
make help # target conf list make ok335xd_evm_android_sdio_defconfig # configure kernel for ok335xd board
Pour compiler :
make ARCH=arm CROSS_COMPILE=arm-eabi- uImage -j8
Cela aboutira à la création d’un ficher :
kernel/arch/arm/boot/uImage
Note : Si vous n’êtes pas trop familier avec la compilation des noyaux Linux, sachez qu’il s’agit ici de la procédure standard, et Internet fourmille de didacticiels à ce sujet.
C’est à ce stade que nous allons pouvoir réaliser nos propres pilotes spécifiques (si nécessaire) pour un accès direct au matériel. Notez cependant, qu’avant de vous attaquer à la conception d’un pilote maison, là encore plusieurs questions sont à se poser:
- Est-ce vraiment d’un pilote dont vous avez besoin ? (Accès registres matériels inaccessibles ? contrainte temps-réel ?)
- Ce pilote n’est-il pas déjà disponible (ou presque) ?
- Disposez-vous de la documentation matérielle précise ? (A priori oui, s’il s’agit d’un truc à vous !)
- Character Driver ? Block Driver ? Network Driver ?
- Kernel module ?
La conception de pilote n’entre pas dans le cadre de cet article, mais voici peut-être un bon point de départ.
6. Compilation d’Android
Android est basé sur une machine virtuelle nommée Dalvik qui permet d’exécuter des bytes code Java (JIT).
Mais dire "Android c’est du Java".... est quelque peu réducteur. En effet, compiler Android, c’est aussi compiler un certain nombre de librairies qui font souvent le pont entre la partie JAVA et LINUX (JNI)
Pour compiler :
make TARGET_PRODUCT=am335xevm OMAPES=4.x -j8
La compilation aboutira à un répertoire (ou jeu de fichiers images) représentant le futur File-System
out/target/product/am335xevm
Tout comme les mécanismes "makefile" des deux sections précédentes, Android a son propre système de build.
Voici les fichiers clefs utilisés lors de la compilation :
6.1 Android.mk
À l’instar du "Makefile" pour "Gnu Make", "Android.mk" est le point d’entrée de la compilation. Dans le cas de notre carte ok335, ce fichier ne contient qu’une seule ligne :
include $(call all-makefiles-under,$(LOCAL_PATH))
Et localement on trouvera en particulier :
- AndroidProducts.mk
- BoardConfig.mk
- CleanSpec.mk
6.2 BoardConfig.mk
Il s’agit des flags et des définitions de compilation. Voici un exemple de ce qu’on peut y trouver :
BOARD_USB_CAMERA := true BOARD_EGL_CFG := device/ti/am335xevm/egl.cfg TARGET_CPU_ABI := armeabi-v7a TARGET_CPU_ABI2 := armeabi TARGET_ARCH := arm TARGET_ARCH_VARIANT := armv7-a-neon ARCH_ARM_HAVE_TLS_REGISTER := true TARGET_NO_KERNEL := true #BOARD_HAVE_BLUETOOTH := true #BOARD_BLUETOOTH_BDROID_BUILDCFG_INCLUDE_DIR := device/ti/am335xevm/bluetooth BOARD_HAVE_BLUETOOTH := false TARGET_NO_BOOTLOADER := true TARGET_NO_RECOVERY := true BOARD_KERNEL_BASE := 0x80000000 #BOARD_KERNEL_CMDLINE := TARGET_NO_RADIOIMAGE := true TARGET_BOARD_PLATFORM := omap3 TARGET_BOOTLOADER_BOARD_NAME := am335xevm USE_OPENGL_RENDERER := true TARGET_HAVE_TSLIB := true
6.3 AndroidProducts.mk
6.3.1 Informations de la carte :
PRODUCT_NAME := am335xevm PRODUCT_DEVICE := am335xevm PRODUCT_BRAND := Android PRODUCT_MODEL := AM335XEVM PRODUCT_MANUFACTURER := Texas_Instruments_Inc
6.3.2 Liste des packages à compiler
PRODUCT_PACKAGE += \ LiveWallpapers \ LiveWallpapersPicker \ MagicSmokeWallpapers
La définition des packages se trouve dans l’arborescence de "/packages". Ainsi à titre d’exemple, le fichier "packages/wallpapers/LivePicker/Android.mk" contient justement :
LOCAL_MODULE_TAGS := optional LOCAL_SRC_FILES := $(call all-subdir-java-files) LOCAL_REQUIRED_MODULES := android.software.live_wallpaper.xml LOCAL_PACKAGE_NAME := LiveWallpapersPicker LOCAL_CERTIFICATE := platform LOCAL_PROGUARD_FLAG_FILES := proguard.flags
6.3.4 Fichiers à recopier tels quels, dans le futur File-System
PRODUCT_COPY_FILES += \ device/ti/am335xevm/init.am335xevm.rc:root/init.am335xevm.rc \ device/ti/am335xevm/media_codecs.xml:system/etc/media_codecs.xml
6.3.5 Propriétés générales
PRODUCT_PROPERTY_OVERRIDES += \ ro.ethernet.default_on=true \ ro.hw.ethernet.onboard=true \ hw.hasethernet=true
La somme des propriétés sera écrite dans le fichier "system/build.prop" du futur File-System
7. Exemple de personnalisation : Changer de pilote wifi
Voici, en deux lignes, comment le wifi fonctionne sous Android :
- Sous Linux, un deamon du nom de "wpa_supplicant" est initialisé (dans init.rc) pour commander le pilote wifi (à travers NL80211 par exemple).
- Sous Android, un pipe de ce deamon est realisé dans "hardware/libhardware_legacy/wifi/wifi.c" afin de fournir une class Java android.net.wifi
Ainsi, il va donc nous falloir modifier Android et son noyau. Voici les étapes par lesquelles il faudrait passer :
- Au niveau du noyau, ajouter le pilote correspondant à votre chipset (s’il n’est pas présent il va falloir l’ajouter)
- Ajuster la section BOARD_WPA_SUPPLICANT_DRIVER dans BoardConfig.mk en conséquence (s’agit-il d’un pilote NL80211, WEXT ?)
- Ajuster/Ajouter une règle dans la cascade des "AndroidProduct.mk" pour copier votre propre fichier de configuration "wpa_supplicant.conf" dans le futur File-System "\etc\wifi\"
- Ajouter une règle dans la cascade des ""AndroidProduct.mk"" pour copier le potentiel firmware propriétaire dans le futur File-System ""\system\etc\firmware\""
Pour plus de détails : http://blog.linuxconsulting.ro/2010/04/porting-wifi-drivers-to-android.html
D’autres exemples de personnalisation : http://www.omappedia.com/wiki/Android_How-tos
8. Android File System patch, une alternative ?
Lorsque l’on observe les points précédents, on remarque que le résultat de la compilation Android sont des fichiers images du futur File-System. En conséquence, il est tout à fait envisageable de :
- Récupérer l’image (typiquement recovery.img) déjà présente sur la carte.
- L’ouvrir à l’aide d’outils adéquats (mkimage, yaffs)
- Créer une rustine en ajoutant un kernel module (.ko), en modifiant le fichier init.rc, ajouter/retirer/modifier des apk, etc. ...
- refermer recovery.img puis re-installation sur la carte.
Dans ce cas de figure, il n’est tout simplement pas nécessaire de compiler Android ! Un gain de temps des plus appréciables !
9. Logo, Boutons et Kiosque
9.1 Logo
Quand on parle de personnalisation, la première chose à laquelle on pense est de pouvoir changer l’animation lors du démarrage d’Android. Voici donc le fichier à créer/modifier dans le futur File-Sytem :
/system/media/bootanimation.zip
Voir : http://forum.xda-developers.com/showthread.php?t=1852621 pour plus de détails
9.2 Kiosque
Si Android est utilisé pour une application bien spécifique, il sera probablement nécessaire de remplacer le Launcher par défaut.
Un Launcher est l’application qui démarre en premier, et qui se réactive lorsque que l’on appuie sur le bouton ""HOME"", souvent appelé :
/system/data/app/Launcher.apk
Elle se distingue des autres apk Android non pas par son nom, mais par son manifest.xml :
<activity android:name="" com.test.mylauncher=""> <intent-filter> <action android.intent.action.main="" android:name=""> <category android.intent.category.home="" android:name=""> <category android.intent.category.default="" android:name=""> </category></category></action></intent-filter> </activity>
9.3 Boutons
Les boutons matériels sont spécifiés ici :
/system/usr/keylayout/*.kl
pour plus d’info : https://source.android.com/devices/input/key-layout-files.html
10. Installation
La manière de ""flasher"" une carte, c’est-a-dire d’appliquer les résultats des points 4, 5, 6 précédents dans une mémoire non volatile (typiquement une mémoire NAND), dépend de la carte choisie.
C’est souvent le ROM-Boot (gravé dans le silicium) qui offrira ce service. Il peut y avoir plusieurs types de transferts possibles :
- UART
- TFTP
- SDCARD
- ...
En général, un interrupteur sur la carte (ou une activité sur l’UART au démarrage) fait passer le ROM-Boot en mode téléchargement avec type de transfert préétabli. Dans le cas de la carte ok335 par exemple, l’interrupteur permet de demander au ROM-Boot de copier les fichiers présents sur une SDCARD (FAT32) vers la mémoire NAND de la carte.
Voici les fichiers présents sur la carte SD :
- u-boot.img - 265kB - le microloader
- uImage - 4212kB - le kernel
- ubi.img- 146304kB - le FileSystem Android
11. Liens
- http://www.xda-developers.com/
- http://www.cyanogenmod.org/
- http://elinux.org/Android_Device