SlideShare une entreprise Scribd logo
1  sur  66
Télécharger pour lire hors ligne
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’implémenter ?
6. Partie Backend
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
● 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
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’implémenter ?
6. Partie Backend
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
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
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
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
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.
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
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
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
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
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
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
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
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
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.
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 ?
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 ?
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 ?
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 ?
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 ?
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 ?
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 ?
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 ?
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 ?
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 ?
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 ?
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
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
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
25
S’interfacer dans l’application Paramètres
private  static  final  String  KEY_SYSTEM_UPDATE_SETTINGS  =  
"system_update_settings";
IMPLÉMENTATION
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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 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
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
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
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 !
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é
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 à différentes versions du software
Et tout ça, c’est le terminal lui-même qui nous le dit
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 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
50
BACKEND
Pour résumer
Quelques captures d’écran de l’outil
51
BACKEND
Des questions ?

Contenu connexe

Similaire à Solution d'OTA

Checklist pour concevoir une application dans le cloud.10 conseils à l'attent...
Checklist pour concevoir une application dans le cloud.10 conseils à l'attent...Checklist pour concevoir une application dans le cloud.10 conseils à l'attent...
Checklist pour concevoir une application dans le cloud.10 conseils à l'attent...Alexandre Touret
 
Checklist pour concevoir une application dans le cloud.10 conseils à l'attent...
Checklist pour concevoir une application dans le cloud.10 conseils à l'attent...Checklist pour concevoir une application dans le cloud.10 conseils à l'attent...
Checklist pour concevoir une application dans le cloud.10 conseils à l'attent...Alexandre Touret
 
#J2Code2018 - Mettez du feu à vos applications avec CodeIgniter
#J2Code2018 - Mettez du feu à vos applications avec CodeIgniter#J2Code2018 - Mettez du feu à vos applications avec CodeIgniter
#J2Code2018 - Mettez du feu à vos applications avec CodeIgniterAtsé François-Xavier KOBON
 
Mise à jour d’un système Linux embarqué « Over The Air »
Mise à jour d’un système Linux embarqué « Over The Air »Mise à jour d’un système Linux embarqué « Over The Air »
Mise à jour d’un système Linux embarqué « Over The Air »Pierre-jean Texier
 
Python application packaging @ MeilleursAgents
Python application packaging @ MeilleursAgentsPython application packaging @ MeilleursAgents
Python application packaging @ MeilleursAgentsNicolas Mussat
 
WordCamp Lyon 2015 - WordPress, Git et l'intégration continue
 WordCamp Lyon 2015 - WordPress, Git et l'intégration continue WordCamp Lyon 2015 - WordPress, Git et l'intégration continue
WordCamp Lyon 2015 - WordPress, Git et l'intégration continueStéphane HULARD
 
Installation Et Configuration De Monkey Spider
Installation Et Configuration De Monkey SpiderInstallation Et Configuration De Monkey Spider
Installation Et Configuration De Monkey SpiderMohamed Ben Bouzid
 
Analyse d'un kernel (crash, core) dump
Analyse d'un kernel (crash, core) dumpAnalyse d'un kernel (crash, core) dump
Analyse d'un kernel (crash, core) dumpGaëtan Trellu
 
Spring Boot & Containers - Do's & Don'ts
Spring Boot & Containers - Do's & Don'tsSpring Boot & Containers - Do's & Don'ts
Spring Boot & Containers - Do's & Don'tsJulien Wittouck
 
Firefox OS de la théorie à la pratique - OSDC
Firefox OS de la théorie à la pratique - OSDCFirefox OS de la théorie à la pratique - OSDC
Firefox OS de la théorie à la pratique - OSDCChristophe Villeneuve
 
"La Performance en Continue" à JMaghreb 3.0 - 05/11/2014
"La Performance en Continue" à JMaghreb 3.0 - 05/11/2014"La Performance en Continue" à JMaghreb 3.0 - 05/11/2014
"La Performance en Continue" à JMaghreb 3.0 - 05/11/2014Benoît de CHATEAUVIEUX
 
Java ME by Amdane Samb at BarCamp Goree, December 2010
Java ME by Amdane Samb at BarCamp Goree, December 2010Java ME by Amdane Samb at BarCamp Goree, December 2010
Java ME by Amdane Samb at BarCamp Goree, December 2010Christelle Scharff
 
Power Shell V2 en action - avec Posh Board 2.0
Power Shell V2 en action - avec Posh Board 2.0Power Shell V2 en action - avec Posh Board 2.0
Power Shell V2 en action - avec Posh Board 2.0Patrick Guimonet
 
Gestion des LOGS savec syslog+loganalyzer
Gestion des LOGS savec syslog+loganalyzerGestion des LOGS savec syslog+loganalyzer
Gestion des LOGS savec syslog+loganalyzerMohamet Lamine DIOP
 
Performance et optimisation de PrestaShop
Performance et optimisation de PrestaShopPerformance et optimisation de PrestaShop
Performance et optimisation de PrestaShopPrestaShop
 
Chaine de production pipeline
Chaine de production   pipelineChaine de production   pipeline
Chaine de production pipelineNicolas wallerand
 
utilisation des core dump sous linux
utilisation des core dump sous linuxutilisation des core dump sous linux
utilisation des core dump sous linuxThierry Gayet
 

Similaire à Solution d'OTA (20)

Checklist pour concevoir une application dans le cloud.10 conseils à l'attent...
Checklist pour concevoir une application dans le cloud.10 conseils à l'attent...Checklist pour concevoir une application dans le cloud.10 conseils à l'attent...
Checklist pour concevoir une application dans le cloud.10 conseils à l'attent...
 
Checklist pour concevoir une application dans le cloud.10 conseils à l'attent...
Checklist pour concevoir une application dans le cloud.10 conseils à l'attent...Checklist pour concevoir une application dans le cloud.10 conseils à l'attent...
Checklist pour concevoir une application dans le cloud.10 conseils à l'attent...
 
iTunes Stats
iTunes StatsiTunes Stats
iTunes Stats
 
#J2Code2018 - Mettez du feu à vos applications avec CodeIgniter
#J2Code2018 - Mettez du feu à vos applications avec CodeIgniter#J2Code2018 - Mettez du feu à vos applications avec CodeIgniter
#J2Code2018 - Mettez du feu à vos applications avec CodeIgniter
 
Mise à jour d’un système Linux embarqué « Over The Air »
Mise à jour d’un système Linux embarqué « Over The Air »Mise à jour d’un système Linux embarqué « Over The Air »
Mise à jour d’un système Linux embarqué « Over The Air »
 
Python application packaging @ MeilleursAgents
Python application packaging @ MeilleursAgentsPython application packaging @ MeilleursAgents
Python application packaging @ MeilleursAgents
 
WordCamp Lyon 2015 - WordPress, Git et l'intégration continue
 WordCamp Lyon 2015 - WordPress, Git et l'intégration continue WordCamp Lyon 2015 - WordPress, Git et l'intégration continue
WordCamp Lyon 2015 - WordPress, Git et l'intégration continue
 
Installation Et Configuration De Monkey Spider
Installation Et Configuration De Monkey SpiderInstallation Et Configuration De Monkey Spider
Installation Et Configuration De Monkey Spider
 
Analyse d'un kernel (crash, core) dump
Analyse d'un kernel (crash, core) dumpAnalyse d'un kernel (crash, core) dump
Analyse d'un kernel (crash, core) dump
 
Spring Boot & Containers - Do's & Don'ts
Spring Boot & Containers - Do's & Don'tsSpring Boot & Containers - Do's & Don'ts
Spring Boot & Containers - Do's & Don'ts
 
Firefox OS de la théorie à la pratique - OSDC
Firefox OS de la théorie à la pratique - OSDCFirefox OS de la théorie à la pratique - OSDC
Firefox OS de la théorie à la pratique - OSDC
 
"La Performance en Continue" à JMaghreb 3.0 - 05/11/2014
"La Performance en Continue" à JMaghreb 3.0 - 05/11/2014"La Performance en Continue" à JMaghreb 3.0 - 05/11/2014
"La Performance en Continue" à JMaghreb 3.0 - 05/11/2014
 
Java ME by Amdane Samb at BarCamp Goree, December 2010
Java ME by Amdane Samb at BarCamp Goree, December 2010Java ME by Amdane Samb at BarCamp Goree, December 2010
Java ME by Amdane Samb at BarCamp Goree, December 2010
 
Power Shell V2 en action - avec Posh Board 2.0
Power Shell V2 en action - avec Posh Board 2.0Power Shell V2 en action - avec Posh Board 2.0
Power Shell V2 en action - avec Posh Board 2.0
 
Push Notification
Push Notification Push Notification
Push Notification
 
Gestion des LOGS savec syslog+loganalyzer
Gestion des LOGS savec syslog+loganalyzerGestion des LOGS savec syslog+loganalyzer
Gestion des LOGS savec syslog+loganalyzer
 
Performance et optimisation de PrestaShop
Performance et optimisation de PrestaShopPerformance et optimisation de PrestaShop
Performance et optimisation de PrestaShop
 
Chaine de production pipeline
Chaine de production   pipelineChaine de production   pipeline
Chaine de production pipeline
 
Android BackupManager
Android BackupManagerAndroid BackupManager
Android BackupManager
 
utilisation des core dump sous linux
utilisation des core dump sous linuxutilisation des core dump sous linux
utilisation des core dump sous linux
 

Solution d'OTA

  • 1. SOLUTION D’OTA Pierre-Olivier Dybman Edouard Marquez +PierreOlivierDybman (aka flappy beard) +EdouardMarquez (aka g123k)
  • 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 QUI SOMMES NOUS ? Bla bla sidereo MONTRÉAL PARIS
  • 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é
  • 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. 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 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 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 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. 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. 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 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 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 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 25 S’interfacer dans l’application Paramètres private  static  final  String  KEY_SYSTEM_UPDATE_SETTINGS  =   "system_update_settings"; IMPLÉMENTATION
  • 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 37 Exécution de la mise à jour IMPLÉMENTATION
  • 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. 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. 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. 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. 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. 42 BACKEND Imaginons : Ca, c’est notre téléphone Android
  • 58. 44 BACKEND Imaginons : Il y en a aussi plein d’autres types
  • 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. 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
  • 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