SOLUTION D’OTA
Pierre-Olivier Dybman Edouard Marquez
+PierreOlivierDybman
(aka flappy beard)
+EdouardMarquez
(aka g123k)
2
PLAN
1. Qui sommes nous ?
2. Qu’est-ce qu’une OTA ?
3. Architecture du système
4. Que contient une OTA ?
5. Comment l’im...
3
QUI SOMMES NOUS ?
Bla bla sidereo
MONTRÉAL
PARIS
4
QUI SOMMES NOUS ?
Nos sujets :
● Applicatif métier mobile
● Gestion de parc
● Gestion applicative
● Objets connectés
● O...
5
QUI SOMMES NOUS ?
6
PLAN
1. Qui sommes nous ?
2. Qu’est-ce qu’une OTA ?
3. Architecture du système
4. Que contient une OTA ?
5. Comment l’im...
QU’EST-CE QU’UNE OTA ?
7
Il faut en réalité parler de
Firmware Over The Air (FOTA).
Cette opération permet de mettre
à jou...
8
Les raisons de lancer une OTA vers un parc de terminaux sont multiples :
Sécurité Bugs
Time to
market
Android
QU’EST-CE ...
9
Mais elle présente des risques, contrairement à une installation
classique par USB :
Batterie Consommation
data
Corrupti...
10
PLAN
1. Qui sommes nous ?
2. Qu’est-ce qu’une OTA ?
3. Architecture du système
4. Que contient une OTA ?
5. Comment l’i...
ARCHITECTURE DU SYSTÈME
11
Bootloader
Programme de bas niveau qui est exécuté au démarrage
du terminal.
Il s’occupe d’init...
ARCHITECTURE DU SYSTÈME
12
Recovery (ou recovery OS)
C’est un OS minimal qui est
spécialisé dans l’exécution de tâches
qu’...
13
Recovery (ou recovery OS)
Selon l’Android Compatibility Definition
Document, le terminal doit intégrer “un
mécanisme pou...
14
Commandes du Recovery
Au lancement, le recovery détecte si le fichier /cache/recovery/command existe.
Si tel est le cas,...
15
Interaction Bootloader-Recovery
Pour que le bootloader comprenne qu’il faut lancer le recovery lorsqu’il y a des
comman...
15
Interaction Bootloader-Recovery
Pour que le bootloader comprenne qu’il faut lancer le recovery lorsqu’il y a des
comman...
15
Interaction Bootloader-Recovery
Pour que le bootloader comprenne qu’il faut lancer le recovery lorsqu’il y a des
comman...
15
Interaction Bootloader-Recovery
Pour que le bootloader comprenne qu’il faut lancer le recovery lorsqu’il y a des
comman...
16
PLAN
1. Qui sommes nous ?
2. Qu’est-ce qu’une OTA ?
3. Architecture du système
4. Que contient une OTA ?
5. Comment l’i...
QUE CONTIENT UNE OTA ?
17
Génération du fichier
AOSP comporte un script python nommé
ota_from_target_files qui génère
automa...
18
Tronc common
META-­‐INF  
    CERT.RSA  
    CERT.SF  
    com  
        android  
            metadata  
            o...
18
Tronc common
META-­‐INF  
    CERT.RSA  
    CERT.SF  
    com  
        android  
            metadata  
            o...
19
Updater-script
Fichier listant des commandes à exécuter :
apply_patch  
apply_patch_check  
delete  /  delete_recursive...
20
mount("ext4",  "EMMC",  "/dev/block/platform/dw_mmc.0/by-­‐name/system",  “/system");  
file_getprop("/system/build.pro...
20
mount("ext4",  "EMMC",  "/dev/block/platform/dw_mmc.0/by-­‐name/system",  “/system");  
file_getprop("/system/build.pro...
20
mount("ext4",  "EMMC",  "/dev/block/platform/dw_mmc.0/by-­‐name/system",  “/system");  
file_getprop("/system/build.pro...
20
mount("ext4",  "EMMC",  "/dev/block/platform/dw_mmc.0/by-­‐name/system",  “/system");  
file_getprop("/system/build.pro...
20
mount("ext4",  "EMMC",  "/dev/block/platform/dw_mmc.0/by-­‐name/system",  “/system");  
file_getprop("/system/build.pro...
20
mount("ext4",  "EMMC",  "/dev/block/platform/dw_mmc.0/by-­‐name/system",  “/system");  
file_getprop("/system/build.pro...
20
mount("ext4",  "EMMC",  "/dev/block/platform/dw_mmc.0/by-­‐name/system",  “/system");  
file_getprop("/system/build.pro...
21
Signature des fichiers OTA
Les applications sont signées grâce à l’outil signapk.
Les fichiers d’OTA sont signés avec le ...
22
PLAN
1. Qui sommes nous ?
2. Qu’est-ce qu’une OTA ?
3. Architecture du système
4. Que contient une OTA ?
5. Comment l’i...
IMPLÉMENTATION
23
Côté Android
Plusieurs étapes vont se succéder afin d’appliquer une mise à jour :
S’interfacer dans le me...
24
S’interfacer dans l’application
Paramètres
A partir du moment où les Google Play Services
sont installés, un écran de m...
25
S’interfacer dans l’application Paramètres
private  static  final  String  KEY_SYSTEM_UPDATE_SETTINGS  =  
"system_upda...
25
S’interfacer dans l’application Paramètres
if  (UserHandle.myUserId()  ==  UserHandle.USER_OWNER)  {  
      Utils.upda...
26
S’interfacer dans l’application Paramètres
      Intent  intent  =  preference.getIntent();  
      if  (intent  !=  nu...
26
S’interfacer dans l’application Paramètres
      Intent  intent  =  preference.getIntent();  
      if  (intent  !=  nu...
27
S’interfacer dans l’application
Paramètres
Il faut donc déclarer une Activity avec cet Intent-
Filter :
<intent-­‐filte...
28
Détecter la présence d’une mise à jour
Requête faite en tâche de fond (Service) et notification à l’utilisateur.
IMPLÉME...
29
Détecter la présence d’une mise à jour
Quelques attributs utiles :
Build.TIME : retourne l’heure de build de la ROM
142...
30
Télécharger l’OTA
L’emplacement du fichier et son nom n’ont pas
d’importance.
On peut se servir du DownloadManager pour
...
31
Télécharger l’OTA
DownloadManager  dm  =  (DownloadManager)  context.getSystemService(Context.DOWNLOAD_SERVICE);  
Down...
32
Télécharger l’OTA
On est ensuite notifié via un Intent de la fin du téléchargement :
<receiver  android:name=".DownloadRe...
32
Télécharger l’OTA
On est ensuite notifié via un Intent de la fin du téléchargement :
<receiver  android:name=".DownloadRe...
33
Lancer la mise à jour
Une fois le fichier à disposition, il faut écrire les commandes pour que le
recovery puisse l’inst...
34
Lancer la mise à jour
public  static  void  installPackage(Context  context,  File  packageFile)  throws  IOException  ...
35
Lancer la mise à jour
private  static  void  bootCommand(Context  context,  String...  args)  throws  IOException  {  
...
36
Lancer la mise à jour
Il faut donc laisser le système faire, sachant que la permission de reboot doit
être demandée, to...
37
Exécution de la mise à jour
IMPLÉMENTATION
38
Supprimer le fichier d’OTA
Le recovery ne supprime pas le fichier de mise à jour, il faut donc le faire à la
main. On ajo...
38
Supprimer le fichier d’OTA
Le recovery ne supprime pas le fichier de mise à jour, il faut donc le faire à la
main. On ajo...
39
PLAN
1. Qui sommes nous ?
2. Qu’est-ce qu’une OTA ?
3. Architecture du système
4. Que contient une OTA ?
5. Comment l’i...
BACKEND
40
La version basique
• Placer les fichiers à disposition des terminaux Android sur le serveur
• Ecrire un JSON à l...
BACKEND
41
La version industrielle
• Doit pouvoir permettre de placer les fichiers à disposition des terminaux
Android sur ...
42
BACKEND
Imaginons :
Ca, c’est notre téléphone Android
43
BACKEND
Imaginons :
Il y en a plein
44
BACKEND
Imaginons :
Il y en a aussi plein d’autres types
45
BACKEND
Imaginons :
Et ils n’ont pas tous la même version de la ROM
Je suis en 0.21bJe suis en 1.01
46
BACKEND
Pour résumer
On a donc :
•Des terminaux
•Qui sont regroupés en une flotte selon leur modèle
•Et qui peuvent être...
47
BACKEND
48
BACKEND
Pour résumer
JSON
49
BACKEND
Ca ne suffit pourtant pas !
Quand on gère un parc conséquent de terminaux Android, on veut pouvoir
tester l’OTA s...
50
BACKEND
Pour résumer
Quelques captures d’écran de l’outil
51
BACKEND
Des questions ?
Prochain SlideShare
Chargement dans…5
×

Solution d'OTA

1 043 vues

Publié le

Conférence donnée à la Droidcon Tunisia 2015.

Les mises à jour OTA sont partout sur nos téléphones (ok, presque partout) mais comment fonctionnent-elles ?

Au cours de cette présentation, nous verrons :

Ce qu’est une MàJ OTA
Comment l’implémenter (exploration de code : intent, permission système, recovery, etc.)
Quelle architecture backend pour mettre en place une solution d’OTA

Publié dans : Technologie
0 commentaire
3 j’aime
Statistiques
Remarques
  • Soyez le premier à commenter

Aucun téléchargement
Vues
Nombre de vues
1 043
Sur SlideShare
0
Issues des intégrations
0
Intégrations
60
Actions
Partages
0
Téléchargements
15
Commentaires
0
J’aime
3
Intégrations 0
Aucune incorporation

Aucune remarque pour cette diapositive

Solution d'OTA

  1. 1. SOLUTION D’OTA Pierre-Olivier Dybman Edouard Marquez +PierreOlivierDybman (aka flappy beard) +EdouardMarquez (aka g123k)
  2. 2. 2 PLAN 1. Qui sommes nous ? 2. Qu’est-ce qu’une OTA ? 3. Architecture du système 4. Que contient une OTA ? 5. Comment l’implémenter ? 6. Partie Backend
  3. 3. 3 QUI SOMMES NOUS ? Bla bla sidereo MONTRÉAL PARIS
  4. 4. 4 QUI SOMMES NOUS ? Nos sujets : ● Applicatif métier mobile ● Gestion de parc ● Gestion applicative ● Objets connectés ● OS Embarqués Nos compétences : ● Étude, conseil et analyse ● Gestion de projets ● Développement et recueil 
 des retours utilisateurs ● Déploiement ● Mise en marché
  5. 5. 5 QUI SOMMES NOUS ?
  6. 6. 6 PLAN 1. Qui sommes nous ? 2. Qu’est-ce qu’une OTA ? 3. Architecture du système 4. Que contient une OTA ? 5. Comment l’implémenter ? 6. Partie Backend
  7. 7. QU’EST-CE QU’UNE OTA ? 7 Il faut en réalité parler de Firmware Over The Air (FOTA). Cette opération permet de mettre à jour des terminaux à distance, sans que l’utilisateur n’ait à les connecter sur un ordinateur. La mise à jour est stockée sur un serveur que le téléphone vient télécharger puis installer.
  8. 8. 8 Les raisons de lancer une OTA vers un parc de terminaux sont multiples : Sécurité Bugs Time to market Android QU’EST-CE QU’UNE OTA ? Service après vente Fonctionnalités
  9. 9. 9 Mais elle présente des risques, contrairement à une installation classique par USB : Batterie Consommation data Corruption des fichiers QU’EST-CE QU’UNE OTA ?
  10. 10. 10 PLAN 1. Qui sommes nous ? 2. Qu’est-ce qu’une OTA ? 3. Architecture du système 4. Que contient une OTA ? 5. Comment l’implémenter ? 6. Partie Backend
  11. 11. ARCHITECTURE DU SYSTÈME 11 Bootloader Programme de bas niveau qui est exécuté au démarrage du terminal. Il s’occupe d’initialiser le matériel puis de lancer le système d’exploitation.
  12. 12. ARCHITECTURE DU SYSTÈME 12 Recovery (ou recovery OS) C’est un OS minimal qui est spécialisé dans l’exécution de tâches qu’Android directement ne peut pas faire : • réinitialisation du terminal • installer des mises à jour Il peut être lancé manuellement (combinaisons de touches) ou via adb : adb  reboot  recovery
  13. 13. 13 Recovery (ou recovery OS) Selon l’Android Compatibility Definition Document, le terminal doit intégrer “un mécanisme pour remplacer le système dans sa globalité” “… il doit supporter des mises à jour sans supprimer les données utilisateur” ARCHITECTURE DU SYSTÈME
  14. 14. 14 Commandes du Recovery Au lancement, le recovery détecte si le fichier /cache/recovery/command existe. Si tel est le cas, il exécutera les commandes les unes à la suite des autres. -­‐-­‐update-­‐package=<path>   -­‐-­‐wipe-­‐data
 -­‐-­‐wipe-­‐cache
 -­‐-­‐locale
 -­‐-­‐send-­‐intent=... Fichier à vérifier puis installer Effacer les partitions userdata et cache Effacer la partition cache Langue pour l’interface utilisateur Envoyer cet Intent lorsqu’Android sera redémarré ARCHITECTURE DU SYSTÈME
  15. 15. 15 Interaction Bootloader-Recovery Pour que le bootloader comprenne qu’il faut lancer le recovery lorsqu’il y a des commandes, il copie ses arguments vers le bootloader control block (BCB). Le format du BCB est défini dans la structure bootloader_message : struct  bootloader_message  {        char  command[32];        char  stage[32];        char  status[32];          char  reserved[224];        char  recovery[768];   } ARCHITECTURE DU SYSTÈME
  16. 16. 15 Interaction Bootloader-Recovery Pour que le bootloader comprenne qu’il faut lancer le recovery lorsqu’il y a des commandes, il copie ses arguments vers le bootloader control block (BCB). Le format du BCB est défini dans la structure bootloader_message : struct  bootloader_message  {        char  command[32];        char  stage[32];        char  status[32];          char  reserved[224];        char  recovery[768];   } La commande pour le bootloader boot-recovery ARCHITECTURE DU SYSTÈME
  17. 17. 15 Interaction Bootloader-Recovery Pour que le bootloader comprenne qu’il faut lancer le recovery lorsqu’il y a des commandes, il copie ses arguments vers le bootloader control block (BCB). Le format du BCB est défini dans la structure bootloader_message : struct  bootloader_message  {        char  command[32];        char  stage[32];        char  status[32];          char  reserved[224];        char  recovery[768];   } Options pour le recovery --update ARCHITECTURE DU SYSTÈME
  18. 18. 15 Interaction Bootloader-Recovery Pour que le bootloader comprenne qu’il faut lancer le recovery lorsqu’il y a des commandes, il copie ses arguments vers le bootloader control block (BCB). Le format du BCB est défini dans la structure bootloader_message : struct  bootloader_message  {        char  command[32];        char  stage[32];        char  status[32];          char  reserved[224];        char  recovery[768];   } Etape de l’installation (si des redémarrages sont nécessaires) ARCHITECTURE DU SYSTÈME
  19. 19. 16 PLAN 1. Qui sommes nous ? 2. Qu’est-ce qu’une OTA ? 3. Architecture du système 4. Que contient une OTA ? 5. Comment l’implémenter ? 6. Partie Backend
  20. 20. QUE CONTIENT UNE OTA ? 17 Génération du fichier AOSP comporte un script python nommé ota_from_target_files qui génère automatiquement un fichier zip installable. Par défaut, il crée une mise à jour complète. Mais il peut aussi produire une mise à jour différentielle en lui donnant la dernière OTA.
  21. 21. 18 Tronc common META-­‐INF      CERT.RSA      CERT.SF      com          android              metadata              otacert          google              android          update-­‐binary          updater-­‐script      MANIFEST.MF QUE CONTIENT UNE OTA ?
  22. 22. 18 Tronc common META-­‐INF      CERT.RSA      CERT.SF      com          android              metadata              otacert          google              android          update-­‐binary          updater-­‐script      MANIFEST.MF Fichier le plus important QUE CONTIENT UNE OTA ?
  23. 23. 19 Updater-script Fichier listant des commandes à exécuter : apply_patch   apply_patch_check   delete  /  delete_recursive   file_getprop   format   mount  /  umount   package_extract_dir  /  package_extract_file   set_metadata  /  set_metadata_recursive   show_progress   symlink   ui_print QUE CONTIENT UNE OTA ?
  24. 24. 20 mount("ext4",  "EMMC",  "/dev/block/platform/dw_mmc.0/by-­‐name/system",  “/system");   file_getprop("/system/build.prop",  "ro.build.fingerprint")  ==  "google/mantaray/manta:4.4.3/ KTU84L/1148727:user/release-­‐keys"  ||          file_getprop("/system/build.prop",  "ro.build.fingerprint")  ==  "google/mantaray/manta:4.4.4/ KTU84P/1227136:user/release-­‐keys"  ||          abort("Package  expects  build  fingerprint  of  google/mantaray/manta:4.4.3/KTU84L/ 1148727:user/release-­‐keys  or  google/mantaray/manta:4.4.4/KTU84P/1227136:user/release-­‐keys;  this   device  has  "  +  getprop("ro.build.fingerprint")  +  ".");
 getprop("ro.product.device")  ==  "manta"  ||  abort("This  package  is  for  "manta"  devices;  this   is  a  ""  +  getprop("ro.product.device")  +  "".");
 ui_print("Verifying  current  system...");   show_progress(0.100000,  0);   apply_patch_check("/system/app/BasicDreams.apk",  "42b5544ed0a896f536c1d58ffe1c68a974f12687",   "67280e2672c034505ba417e6c0054be375415c08")  ||  abort(""/system/app/BasicDreams.apk"  has   unexpected  contents.");   set_progress(0.000065);   set_metadata_recursive("/system/xbin",  "uid",  0,  "gid",  2000,  "dmode",  0755,  "fmode",  0755,   "capabilities",  0x0,  "selabel",  "u:object_r:system_file:s0");   ui_print("Patching  remaining  system  files...");   apply_patch("/system/build.prop",  "-­‐",                d7978a543a5677b77f7c1b15b5f583fb8fa14bd8,  2774,                4d220efc37eb1786bbb379ae31145bf7edd176d9,  package_extract_file("patch/system/ build.prop.p"));   set_metadata("/system/build.prop",  "uid",  0,  "gid",  0,  "mode",  0644,  "capabilities",  0x0);   unmount("/system"); QUE CONTIENT UNE OTA ?
  25. 25. 20 mount("ext4",  "EMMC",  "/dev/block/platform/dw_mmc.0/by-­‐name/system",  “/system");   file_getprop("/system/build.prop",  "ro.build.fingerprint")  ==  "google/mantaray/manta:4.4.3/ KTU84L/1148727:user/release-­‐keys"  ||          file_getprop("/system/build.prop",  "ro.build.fingerprint")  ==  "google/mantaray/manta:4.4.4/ KTU84P/1227136:user/release-­‐keys"  ||          abort("Package  expects  build  fingerprint  of  google/mantaray/manta:4.4.3/KTU84L/ 1148727:user/release-­‐keys  or  google/mantaray/manta:4.4.4/KTU84P/1227136:user/release-­‐keys;  this   device  has  "  +  getprop("ro.build.fingerprint")  +  ".");
 getprop("ro.product.device")  ==  "manta"  ||  abort("This  package  is  for  "manta"  devices;  this   is  a  ""  +  getprop("ro.product.device")  +  "".");
 ui_print("Verifying  current  system...");   show_progress(0.100000,  0);   apply_patch_check("/system/app/BasicDreams.apk",  "42b5544ed0a896f536c1d58ffe1c68a974f12687",   "67280e2672c034505ba417e6c0054be375415c08")  ||  abort(""/system/app/BasicDreams.apk"  has   unexpected  contents.");   set_progress(0.000065);   set_metadata_recursive("/system/xbin",  "uid",  0,  "gid",  2000,  "dmode",  0755,  "fmode",  0755,   "capabilities",  0x0,  "selabel",  "u:object_r:system_file:s0");   ui_print("Patching  remaining  system  files...");   apply_patch("/system/build.prop",  "-­‐",                d7978a543a5677b77f7c1b15b5f583fb8fa14bd8,  2774,                4d220efc37eb1786bbb379ae31145bf7edd176d9,  package_extract_file("patch/system/ build.prop.p"));   set_metadata("/system/build.prop",  "uid",  0,  "gid",  0,  "mode",  0644,  "capabilities",  0x0);   unmount("/system"); Monter une partition QUE CONTIENT UNE OTA ?
  26. 26. 20 mount("ext4",  "EMMC",  "/dev/block/platform/dw_mmc.0/by-­‐name/system",  “/system");   file_getprop("/system/build.prop",  "ro.build.fingerprint")  ==  "google/mantaray/manta:4.4.3/ KTU84L/1148727:user/release-­‐keys"  ||          file_getprop("/system/build.prop",  "ro.build.fingerprint")  ==  "google/mantaray/manta:4.4.4/ KTU84P/1227136:user/release-­‐keys"  ||          abort("Package  expects  build  fingerprint  of  google/mantaray/manta:4.4.3/KTU84L/ 1148727:user/release-­‐keys  or  google/mantaray/manta:4.4.4/KTU84P/1227136:user/release-­‐keys;  this   device  has  "  +  getprop("ro.build.fingerprint")  +  ".");
 getprop("ro.product.device")  ==  "manta"  ||  abort("This  package  is  for  "manta"  devices;  this   is  a  ""  +  getprop("ro.product.device")  +  "".");
 ui_print("Verifying  current  system...");   show_progress(0.100000,  0);   apply_patch_check("/system/app/BasicDreams.apk",  "42b5544ed0a896f536c1d58ffe1c68a974f12687",   "67280e2672c034505ba417e6c0054be375415c08")  ||  abort(""/system/app/BasicDreams.apk"  has   unexpected  contents.");   set_progress(0.000065);   set_metadata_recursive("/system/xbin",  "uid",  0,  "gid",  2000,  "dmode",  0755,  "fmode",  0755,   "capabilities",  0x0,  "selabel",  "u:object_r:system_file:s0");   ui_print("Patching  remaining  system  files...");   apply_patch("/system/build.prop",  "-­‐",                d7978a543a5677b77f7c1b15b5f583fb8fa14bd8,  2774,                4d220efc37eb1786bbb379ae31145bf7edd176d9,  package_extract_file("patch/system/ build.prop.p"));   set_metadata("/system/build.prop",  "uid",  0,  "gid",  0,  "mode",  0644,  "capabilities",  0x0);   unmount("/system"); Obtenir une propriété système dans un fichier contenant des propriétés QUE CONTIENT UNE OTA ?
  27. 27. 20 mount("ext4",  "EMMC",  "/dev/block/platform/dw_mmc.0/by-­‐name/system",  “/system");   file_getprop("/system/build.prop",  "ro.build.fingerprint")  ==  "google/mantaray/manta:4.4.3/ KTU84L/1148727:user/release-­‐keys"  ||          file_getprop("/system/build.prop",  "ro.build.fingerprint")  ==  "google/mantaray/manta:4.4.4/ KTU84P/1227136:user/release-­‐keys"  ||          abort("Package  expects  build  fingerprint  of  google/mantaray/manta:4.4.3/KTU84L/ 1148727:user/release-­‐keys  or  google/mantaray/manta:4.4.4/KTU84P/1227136:user/release-­‐keys;  this   device  has  "  +  getprop("ro.build.fingerprint")  +  ".");
 getprop("ro.product.device")  ==  "manta"  ||  abort("This  package  is  for  "manta"  devices;  this   is  a  ""  +  getprop("ro.product.device")  +  "".");
 ui_print("Verifying  current  system...");   show_progress(0.100000,  0);   apply_patch_check("/system/app/BasicDreams.apk",  "42b5544ed0a896f536c1d58ffe1c68a974f12687",   "67280e2672c034505ba417e6c0054be375415c08")  ||  abort(""/system/app/BasicDreams.apk"  has   unexpected  contents.");   set_progress(0.000065);   set_metadata_recursive("/system/xbin",  "uid",  0,  "gid",  2000,  "dmode",  0755,  "fmode",  0755,   "capabilities",  0x0,  "selabel",  "u:object_r:system_file:s0");   ui_print("Patching  remaining  system  files...");   apply_patch("/system/build.prop",  "-­‐",                d7978a543a5677b77f7c1b15b5f583fb8fa14bd8,  2774,                4d220efc37eb1786bbb379ae31145bf7edd176d9,  package_extract_file("patch/system/ build.prop.p"));   set_metadata("/system/build.prop",  "uid",  0,  "gid",  0,  "mode",  0644,  "capabilities",  0x0);   unmount("/system"); Obtenir une propriété du système QUE CONTIENT UNE OTA ?
  28. 28. 20 mount("ext4",  "EMMC",  "/dev/block/platform/dw_mmc.0/by-­‐name/system",  “/system");   file_getprop("/system/build.prop",  "ro.build.fingerprint")  ==  "google/mantaray/manta:4.4.3/ KTU84L/1148727:user/release-­‐keys"  ||          file_getprop("/system/build.prop",  "ro.build.fingerprint")  ==  "google/mantaray/manta:4.4.4/ KTU84P/1227136:user/release-­‐keys"  ||          abort("Package  expects  build  fingerprint  of  google/mantaray/manta:4.4.3/KTU84L/ 1148727:user/release-­‐keys  or  google/mantaray/manta:4.4.4/KTU84P/1227136:user/release-­‐keys;  this   device  has  "  +  getprop("ro.build.fingerprint")  +  ".");
 getprop("ro.product.device")  ==  "manta"  ||  abort("This  package  is  for  "manta"  devices;  this   is  a  ""  +  getprop("ro.product.device")  +  "".");
 ui_print("Verifying  current  system...");   show_progress(0.100000,  0);   apply_patch_check("/system/app/BasicDreams.apk",  "42b5544ed0a896f536c1d58ffe1c68a974f12687",   "67280e2672c034505ba417e6c0054be375415c08")  ||  abort(""/system/app/BasicDreams.apk"  has   unexpected  contents.");   set_progress(0.000065);   set_metadata_recursive("/system/xbin",  "uid",  0,  "gid",  2000,  "dmode",  0755,  "fmode",  0755,   "capabilities",  0x0,  "selabel",  "u:object_r:system_file:s0");   ui_print("Patching  remaining  system  files...");   apply_patch("/system/build.prop",  "-­‐",                d7978a543a5677b77f7c1b15b5f583fb8fa14bd8,  2774,                4d220efc37eb1786bbb379ae31145bf7edd176d9,  package_extract_file("patch/system/ build.prop.p"));   set_metadata("/system/build.prop",  "uid",  0,  "gid",  0,  "mode",  0644,  "capabilities",  0x0);   unmount("/system"); Application d’un patch sur un fichier ou une partition avec vérification des hashs sur les fichiers QUE CONTIENT UNE OTA ?
  29. 29. 20 mount("ext4",  "EMMC",  "/dev/block/platform/dw_mmc.0/by-­‐name/system",  “/system");   file_getprop("/system/build.prop",  "ro.build.fingerprint")  ==  "google/mantaray/manta:4.4.3/ KTU84L/1148727:user/release-­‐keys"  ||          file_getprop("/system/build.prop",  "ro.build.fingerprint")  ==  "google/mantaray/manta:4.4.4/ KTU84P/1227136:user/release-­‐keys"  ||          abort("Package  expects  build  fingerprint  of  google/mantaray/manta:4.4.3/KTU84L/ 1148727:user/release-­‐keys  or  google/mantaray/manta:4.4.4/KTU84P/1227136:user/release-­‐keys;  this   device  has  "  +  getprop("ro.build.fingerprint")  +  ".");
 getprop("ro.product.device")  ==  "manta"  ||  abort("This  package  is  for  "manta"  devices;  this   is  a  ""  +  getprop("ro.product.device")  +  "".");
 ui_print("Verifying  current  system...");   show_progress(0.100000,  0);   apply_patch_check("/system/app/BasicDreams.apk",  "42b5544ed0a896f536c1d58ffe1c68a974f12687",   "67280e2672c034505ba417e6c0054be375415c08")  ||  abort(""/system/app/BasicDreams.apk"  has   unexpected  contents.");   set_progress(0.000065);   set_metadata_recursive("/system/xbin",  "uid",  0,  "gid",  2000,  "dmode",  0755,  "fmode",  0755,   "capabilities",  0x0,  "selabel",  "u:object_r:system_file:s0");   ui_print("Patching  remaining  system  files...");   apply_patch("/system/build.prop",  "-­‐",                d7978a543a5677b77f7c1b15b5f583fb8fa14bd8,  2774,                4d220efc37eb1786bbb379ae31145bf7edd176d9,  package_extract_file("patch/system/ build.prop.p"));   set_metadata("/system/build.prop",  "uid",  0,  "gid",  0,  "mode",  0644,  "capabilities",  0x0);   unmount("/system"); Appliquer sur un fichier ou un dossier des droits de manière récursive (utilisateur/groupe/ SELinux…) QUE CONTIENT UNE OTA ?
  30. 30. 20 mount("ext4",  "EMMC",  "/dev/block/platform/dw_mmc.0/by-­‐name/system",  “/system");   file_getprop("/system/build.prop",  "ro.build.fingerprint")  ==  "google/mantaray/manta:4.4.3/ KTU84L/1148727:user/release-­‐keys"  ||          file_getprop("/system/build.prop",  "ro.build.fingerprint")  ==  "google/mantaray/manta:4.4.4/ KTU84P/1227136:user/release-­‐keys"  ||          abort("Package  expects  build  fingerprint  of  google/mantaray/manta:4.4.3/KTU84L/ 1148727:user/release-­‐keys  or  google/mantaray/manta:4.4.4/KTU84P/1227136:user/release-­‐keys;  this   device  has  "  +  getprop("ro.build.fingerprint")  +  ".");
 getprop("ro.product.device")  ==  "manta"  ||  abort("This  package  is  for  "manta"  devices;  this   is  a  ""  +  getprop("ro.product.device")  +  "".");
 ui_print("Verifying  current  system...");   show_progress(0.100000,  0);   apply_patch_check("/system/app/BasicDreams.apk",  "42b5544ed0a896f536c1d58ffe1c68a974f12687",   "67280e2672c034505ba417e6c0054be375415c08")  ||  abort(""/system/app/BasicDreams.apk"  has   unexpected  contents.");   set_progress(0.000065);   set_metadata_recursive("/system/xbin",  "uid",  0,  "gid",  2000,  "dmode",  0755,  "fmode",  0755,   "capabilities",  0x0,  "selabel",  "u:object_r:system_file:s0");   ui_print("Patching  remaining  system  files...");   apply_patch("/system/build.prop",  "-­‐",                d7978a543a5677b77f7c1b15b5f583fb8fa14bd8,  2774,                4d220efc37eb1786bbb379ae31145bf7edd176d9,  package_extract_file("patch/system/ build.prop.p"));   set_metadata("/system/build.prop",  "uid",  0,  "gid",  0,  "mode",  0644,  "capabilities",  0x0);   unmount("/system"); Démonter un volume QUE CONTIENT UNE OTA ?
  31. 31. 21 Signature des fichiers OTA Les applications sont signées grâce à l’outil signapk. Les fichiers d’OTA sont signés avec le même utilitaire, mais avec une option spéciale (-w) qui permet de signer le fichier dans sa globalité et non chacun d’entre eux. Cette signature est ensuite vérifiée à deux reprises : •Lorsqu’Android va écrire dans le fichier de commande du recovery •Dans le recovery, avant de flasher l’image Le dossier META-INF contient notamment le certificat de mise à jour (au format PEM). Il est possible/conseillé d’utiliser une clé différente de celles utilisées dans le reste du système. QUE CONTIENT UNE OTA ?
  32. 32. 22 PLAN 1. Qui sommes nous ? 2. Qu’est-ce qu’une OTA ? 3. Architecture du système 4. Que contient une OTA ? 5. Comment l’implémenter ? 6. Partie Backend
  33. 33. IMPLÉMENTATION 23 Côté Android Plusieurs étapes vont se succéder afin d’appliquer une mise à jour : S’interfacer dans le menu Paramètres Détecter la présence d’une mise à jour Télécharger la mise à jour Installer la mise à jour Supprimer la mise à jour, une fois installée
  34. 34. 24 S’interfacer dans l’application Paramètres A partir du moment où les Google Play Services sont installés, un écran de mise à jour est forcément disponible dans les Paramètres. IMPLÉMENTATION
  35. 35. 25 S’interfacer dans l’application Paramètres private  static  final  String  KEY_SYSTEM_UPDATE_SETTINGS  =   "system_update_settings"; IMPLÉMENTATION
  36. 36. 25 S’interfacer dans l’application Paramètres if  (UserHandle.myUserId()  ==  UserHandle.USER_OWNER)  {        Utils.updatePreferenceToSpecificActivityOrRemove(act,  
        parentPreference,          KEY_SYSTEM_UPDATE_SETTINGS,
        Utils.UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY);   }  else  {        //  Remove  for  secondary  users        removePreference(KEY_SYSTEM_UPDATE_SETTINGS);   } private  static  final  String  KEY_SYSTEM_UPDATE_SETTINGS  =   "system_update_settings"; IMPLÉMENTATION
  37. 37. 26 S’interfacer dans l’application Paramètres      Intent  intent  =  preference.getIntent();        if  (intent  !=  null)  {                //  Find  the  activity  that  is  in  the  system  image                PackageManager  pm  =  context.getPackageManager();                List<ResolveInfo>  list  =  pm.queryIntentActivities(intent,  0);                int  listSize  =  list.size();                for  (int  i  =  0;  i  <  listSize;  i++)  {              ResolveInfo  resolveInfo  =  list.get(i);              if  ((resolveInfo.activityInfo.applicationInfo.flags  &  ApplicationInfo.FLAG_SYSTEM)                    !=  0)  {                      //  Replace  the  intent  with  this  specific  activity                      preference.setIntent(new  Intent().setClassName(                            resolveInfo.activityInfo.packageName,                            resolveInfo.activityInfo.name));                      if  ((flags  &  UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY)  !=  0)  {                    //  Set  the  preference  title  to  the  activity's  label                    preference.setTitle(resolveInfo.loadLabel(pm));                      }                      return  true;              }                }        } IMPLÉMENTATION
  38. 38. 26 S’interfacer dans l’application Paramètres      Intent  intent  =  preference.getIntent();        if  (intent  !=  null)  {                //  Find  the  activity  that  is  in  the  system  image                PackageManager  pm  =  context.getPackageManager();                List<ResolveInfo>  list  =  pm.queryIntentActivities(intent,  0);                int  listSize  =  list.size();                for  (int  i  =  0;  i  <  listSize;  i++)  {              ResolveInfo  resolveInfo  =  list.get(i);              if  ((resolveInfo.activityInfo.applicationInfo.flags  &  ApplicationInfo.FLAG_SYSTEM)                    !=  0)  {                      //  Replace  the  intent  with  this  specific  activity                      preference.setIntent(new  Intent().setClassName(                            resolveInfo.activityInfo.packageName,                            resolveInfo.activityInfo.name));                      if  ((flags  &  UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY)  !=  0)  {                    //  Set  the  preference  title  to  the  activity's  label                    preference.setTitle(resolveInfo.loadLabel(pm));                      }                      return  true;              }                }        } Le nom de l’application sera celui 
 affiché dans les Paramètres. IMPLÉMENTATION
  39. 39. 27 S’interfacer dans l’application Paramètres Il faut donc déclarer une Activity avec cet Intent- Filter : <intent-­‐filter  android:priority="999">          <action  android:name="android.settings.SYSTEM_UPDATE_SETTINGS"  />          <category  android:name="android.intent.category.DEFAULT"  />   </intent-­‐filter> Il faut utiliser une priorité plus élevée que l’Activity des Play Services IMPLÉMENTATION
  40. 40. 28 Détecter la présence d’une mise à jour Requête faite en tâche de fond (Service) et notification à l’utilisateur. IMPLÉMENTATION
  41. 41. 29 Détecter la présence d’une mise à jour Quelques attributs utiles : Build.TIME : retourne l’heure de build de la ROM 1424637868000 Build.DISPLAY : retourne le nom de la ROM (côté utilisateur) VITAMIN_A_V1.0.1_beta2 Build.FINGERPRINT : retourne le nom complet de la ROM Vitamin/VitaminA/VitaminA:4.4.4/KTU84P/20150222.214246:user/release-keys IMPLÉMENTATION
  42. 42. 30 Télécharger l’OTA L’emplacement du fichier et son nom n’ont pas d’importance. On peut se servir du DownloadManager pour cette tâche, mais il pourra uniquement télécharger dans les répertoires publics. IMPLÉMENTATION
  43. 43. 31 Télécharger l’OTA DownloadManager  dm  =  (DownloadManager)  context.getSystemService(Context.DOWNLOAD_SERVICE);   DownloadManager.Request  requestDownload  =  new  DownloadManager.Request(Uri.parse(update.url))        .setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI)        .setNotificationVisibility(DownloadManager.Request.VISIBILITY_HIDDEN)        .setVisibleInDownloadsUi(false)        .setTitle(update.buildNumber)        .setDestinationUri(getDestinationUri())        .setDescription(update.buildNumber);   return  dm.enqueue(requestDownload); <uses-­‐permission  android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION"  />   <uses-­‐permission  android:name="android.permission.ACCESS_DOWNLOAD_MANAGER"  />   <uses-­‐permission  android:name="android.permission.ACCESS_DOWNLOAD_MANAGER_ADVANCED"  /> Et demander ces permissions : IMPLÉMENTATION
  44. 44. 32 Télécharger l’OTA On est ensuite notifié via un Intent de la fin du téléchargement : <receiver  android:name=".DownloadReceiver">        <intent-­‐filter>              <action  android:name="android.intent.action.DOWNLOAD_COMPLETE"  />        </intent-­‐filter>   </receiver> IMPLÉMENTATION
  45. 45. 32 Télécharger l’OTA On est ensuite notifié via un Intent de la fin du téléchargement : <receiver  android:name=".DownloadReceiver">        <intent-­‐filter>              <action  android:name="android.intent.action.DOWNLOAD_COMPLETE"  />        </intent-­‐filter>   </receiver> Il ne faut pas oublier de vérifier que le fichier est valide. IMPLÉMENTATION
  46. 46. 33 Lancer la mise à jour Une fois le fichier à disposition, il faut écrire les commandes pour que le recovery puisse l’installer. Il n’y a pas besoin de le faire à la main, car la classe RecoverySystem (API 8) s’en occupe. Plusieurs méthodes sont disponibles : public  static  void  installPackage  (Context  context,  File  packageFile)
 public  static  void  rebootWipeCache  (Context  context)
 public  static  void  rebootWipeUserData  (Context  context)
 public  static  void  verifyPackage  (File  packageFile,  
                          RecoverySystem.ProgressListener  listener,  File  deviceCertsZipFile) IMPLÉMENTATION
  47. 47. 34 Lancer la mise à jour public  static  void  installPackage(Context  context,  File  packageFile)  throws  IOException  {
      String  filename  =  packageFile.getCanonicalPath();        Log.w(TAG,  "!!!  REBOOTING  TO  INSTALL  "  +  filename  +  "  !!!");        final  String  filenameArg  =  "-­‐-­‐update_package="  +  filename;        final  String  localeArg  =  "-­‐-­‐locale="  +  Locale.getDefault().toString();        bootCommand(context,  filenameArg,  localeArg);   } IMPLÉMENTATION
  48. 48. 35 Lancer la mise à jour private  static  void  bootCommand(Context  context,  String...  args)  throws  IOException  {                  RECOVERY_DIR.mkdirs();    //  In  case  we  need  it                  COMMAND_FILE.delete();    //  In  case  it's  not  writable                  LOG_FILE.delete();                  FileWriter  command  =  new  FileWriter(COMMAND_FILE);                  try  {                          for  (String  arg  :  args)  {                                  if  (!TextUtils.isEmpty(arg))  {                                          command.write(arg);                                          command.write("n");                                  }                          }                  }  finally  {                          command.close();                  }                  //  Having  written  the  command  file,  go  ahead  and  reboot                  PowerManager  pm  =  (PowerManager)  context.getSystemService(Context.POWER_SERVICE);                  pm.reboot(PowerManager.REBOOT_RECOVERY);                  throw  new  IOException("Reboot  failed  (no  permissions?)");          } IMPLÉMENTATION
  49. 49. 36 Lancer la mise à jour Il faut donc laisser le système faire, sachant que la permission de reboot doit être demandée, tout comme celle pour écrire sur la partition du cache : <uses-­‐permission  android:name="android.permission.REBOOT"  />   <uses-­‐permission  android:name="android.permission.ACCESS_CACHE_FILESYSTEM"  /> IMPLÉMENTATION
  50. 50. 37 Exécution de la mise à jour IMPLÉMENTATION
  51. 51. 38 Supprimer le fichier d’OTA Le recovery ne supprime pas le fichier de mise à jour, il faut donc le faire à la main. On ajoute pour cela un Intent-Filter : <receiver  android:name=".BootReceiver">        <intent-­‐filter>              <action  android:name="android.intent.action.PRE_BOOT_COMPLETED"  />        </intent-­‐filter>   </receiver> IMPLÉMENTATION
  52. 52. 38 Supprimer le fichier d’OTA Le recovery ne supprime pas le fichier de mise à jour, il faut donc le faire à la main. On ajoute pour cela un Intent-Filter : <receiver  android:name=".BootReceiver">        <intent-­‐filter>              <action  android:name="android.intent.action.PRE_BOOT_COMPLETED"  />        </intent-­‐filter>   </receiver> … où l’on vérifie que le terminal dispose bien de la nouvelle ROM IMPLÉMENTATION
  53. 53. 39 PLAN 1. Qui sommes nous ? 2. Qu’est-ce qu’une OTA ? 3. Architecture du système 4. Que contient une OTA ? 5. Comment l’implémenter ? 6. Partie Backend
  54. 54. BACKEND 40 La version basique • Placer les fichiers à disposition des terminaux Android sur le serveur • Ecrire un JSON à la main qui liste les fichiers d’OTA et les infos utiles (date de build, etc.) • C’est tout !
  55. 55. BACKEND 41 La version industrielle • Doit pouvoir permettre de placer les fichiers à disposition des terminaux Android sur le serveur • Doit fournir une API aux terminaux Android afin de leur servir la bonne OTA • Doit pouvoir gérer plusieurs types de terminaux • Ainsi que plusieurs branches de distribution • Doit nous permettre d’avoir des statistiques sur le parc géré
  56. 56. 42 BACKEND Imaginons : Ca, c’est notre téléphone Android
  57. 57. 43 BACKEND Imaginons : Il y en a plein
  58. 58. 44 BACKEND Imaginons : Il y en a aussi plein d’autres types
  59. 59. 45 BACKEND Imaginons : Et ils n’ont pas tous la même version de la ROM Je suis en 0.21bJe suis en 1.01
  60. 60. 46 BACKEND Pour résumer On a donc : •Des terminaux •Qui sont regroupés en une flotte selon leur modèle •Et qui peuvent être à différentes versions du software Et tout ça, c’est le terminal lui-même qui nous le dit
  61. 61. 47 BACKEND
  62. 62. 48 BACKEND Pour résumer JSON
  63. 63. 49 BACKEND Ca ne suffit pourtant pas ! Quand on gère un parc conséquent de terminaux Android, on veut pouvoir tester l’OTA sur un échantillon avant de la proposer à tout le monde. On veut donc pouvoir créer des branches de distribution. Cela permet : • d’isoler des terminaux (téléphones de développement, modèles pas encore sortis, tablettes déposées au SAV) • de distribuer une OTA à une sous-partie du parc de téléphone
  64. 64. 50 BACKEND Pour résumer Quelques captures d’écran de l’outil
  65. 65. 51 BACKEND
  66. 66. Des questions ?

×