Code spécifique à l'appareil

Le système de récupération comprend plusieurs hooks pour insérer le code spécifique à l'appareil afin que certaines mises à jour peuvent également mettre à jour d'autres parties de l'appareil que le système Android (par exemple, la bande de base). ou un processeur radio).

Les sections et exemples suivants personnalisent l'appareil tardis généré par l'API yoyodyne.

Mappage des partitions

À partir d'Android 2.3, la plate-forme prend en charge les périphériques flash eMMc et le système de fichiers ext4 qui exécute sur ces appareils. Il est également compatible avec les périphériques flash Memory Technology Device (MTD) et yaffs2. système de fichiers des versions antérieures.

Le fichier de mappage de partition est spécifié par TARGET_RECOVERY_FSTAB ; ce fichier est utilisé à la fois le binaire de récupération et les outils de création de packages. Vous pouvez spécifier le nom du fichier de carte dans TARGET_RECOVERY_FSTAB dans BoardConfig.mk.

Voici un exemple de fichier de mappage de partition:

device/yoyodyne/tardis/recovery.fstab
# mount point       fstype  device       [device2]        [options (3.0+ only)]

/sdcard     vfat    /dev/block/mmcblk0p1 /dev/block/mmcblk0
/cache      yaffs2  cache
/misc       mtd misc
/boot       mtd boot
/recovery   emmc    /dev/block/platform/s3c-sdhci.0/by-name/recovery
/system     ext4    /dev/block/platform/s3c-sdhci.0/by-name/system length=-4096
/data       ext4    /dev/block/platform/s3c-sdhci.0/by-name/userdata

À l'exception de /sdcard, qui est facultatif, tous les points d'installation de ce doit être défini (les appareils peuvent également ajouter des partitions supplémentaires). Il existe cinq types systèmes de fichiers:

yaffs2
Système de fichiers yaffs2 au-dessus d'un périphérique flash MTD. "appareil" doit être le nom de la partition MTD et doit apparaître dans /proc/mtd.
Mtd
Une partition MTD brute, utilisée pour les partitions de démarrage telles que le démarrage et la récupération. La valeur MTD n'est pas réellement monté, mais le point d’installation est utilisé comme clé pour localiser la partition. "appareil" doit être le nom de la partition MTD dans /proc/mtd.
ext4
Système de fichiers ext4 au-dessus d'un périphérique flash eMMc. "appareil" doit être le chemin d'accès de l'appareil de stockage en mode bloc.
Emmc
Un périphérique de stockage en mode bloc eMMc brut, utilisé pour les partitions de démarrage telles que le démarrage et la récupération. Semblable à le type mtd, eMMc n'est jamais réellement installé, mais la chaîne du point d’installation est utilisée pour localiser l'appareil dans le tableau.
vfat
Système de fichiers FAT installé sur un périphérique de stockage en mode bloc, généralement destiné à un stockage externe comme une carte SD. La l’appareil est l’appareil de traitement par blocs ; device2 est un deuxième périphérique en mode bloc que le système tente d'installer si l'installation de l'appareil principal échoue (pour des raisons de compatibilité avec les cartes SD, formatées avec une table de partition).

Toutes les partitions doivent être installées dans le répertoire racine (la valeur du point d'installation doit commencer par une barre oblique et n'avoir aucune autre barre oblique). Cette restriction ne s'applique qu'au montage les systèmes de fichiers en cours de récupération ; le système principal est libre de les installer n'importe où. Les répertoires /boot, /recovery et /misc doivent être des types bruts (mtd ou emmc), tandis que les répertoires /system, /data, /cache et /sdcard (le cas échéant) doivent être des types de systèmes de fichiers (yaffs2, ext4 ou vfat).

À partir d'Android 3.0, le fichier recovery.fstab comprend un champ facultatif supplémentaire, . Actuellement, la seule option définie est length , qui vous permet de définir explicitement spécifier la longueur de la partition. Cette longueur est utilisée lors du reformatage de la partition. (par exemple, pour la partition de données utilisateur lors d'une opération d'effacement de données ou de rétablissement de la configuration d'usine, ou pour partition système lors de l'installation d'un package OTA complet). Si la valeur de longueur est négative, alors la taille à formater est prise en ajoutant la valeur de longueur à la taille réelle de la partition. Pour en définissant "length=-16384" signifie que les 16 derniers kilo-octets de cette partition ne seront pas écrasée lorsque cette partition est reformatée. Il est compatible avec des fonctionnalités telles que le chiffrement la partition de données utilisateur (où les métadonnées de chiffrement sont stockées à la fin de la partition, ne doivent pas être écrasées).

Remarque:Les champs device2 et options sont facultatifs. toute ambiguïté lors de l'analyse. Si l'entrée dans le quatrième champ de la ligne commence par un "/" il est considéré comme une entrée device2 . si l'entrée ne commence pas par "/" il est considéré comme un champ options.

Animation au démarrage

Les fabricants d'appareils ont la possibilité de personnaliser l'animation affichée lorsqu'un appareil Android démarre. Pour ce faire, créez un fichier .zip organisé et localisé en fonction du des spécifications dans bootanimation format.

Pour les appareils Android Things ; vous pouvez importer le fichier compressé dans la console Android Things pour que les images soient incluses dans le produit sélectionné.

Remarque:Ces images doivent respecter les consignes relatives à la marque Android. Pour les consignes relatives à la marque, reportez-vous à la section consacrée à Android Marketing pour les partenaires Hub.

UI de récupération

Pour prendre en charge les appareils avec différents matériels disponibles (boutons physiques, LED, écrans, etc.), vous pouvez personnaliser l'interface de récupération pour afficher l'état et accéder fonctionnalités cachées pour chaque appareil.

L'objectif est de créer une petite bibliothèque statique avec quelques objets C++ pour fournir des fonctionnalités propres à l'appareil. Fichier bootable/recovery/default_device.cpp est utilisé par défaut et constitue une bonne point de départ à copier lors de l’écriture d’une version de ce fichier pour votre appareil.

Remarque:Il est possible que le message Aucune commande s'affiche ici. Activer/Désactiver appuyez de manière prolongée sur le bouton Marche/Arrêt pendant que vous appuyez sur le bouton volume+. Si vos appareils ne disposent pas sur les deux boutons, appuyez de manière prolongée sur n'importe quel bouton pour activer/désactiver le texte.

device/yoyodyne/tardis/recovery/recovery_ui.cpp
#include <linux/input.h>

#include "common.h"
#include "device.h"
#include "screen_ui.h"

Fonctions d'en-tête et d'élément

La classe Device nécessite des fonctions pour renvoyer les en-têtes et les éléments qui apparaissent dans le bloc menu de récupération. Les en-têtes décrivent le fonctionnement du menu (les commandes permettant de modifier/sélectionner l'élément en surbrillance).

static const char* HEADERS[] = { "Volume up/down to move highlight;",
                                 "power button to select.",
                                 "",
                                 NULL };

static const char* ITEMS[] =  {"reboot system now",
                               "apply update from ADB",
                               "wipe data/factory reset",
                               "wipe cache partition",
                               NULL };

Remarque:Les longues lignes sont tronquées (elles ne sont pas renvoyées à la ligne automatiquement). Conservez donc la largeur l’écran de votre appareil à l’esprit.

Personnaliser CheckKey

Ensuite, définissez l'implémentation RecoveryUI de votre appareil. Cet exemple part du principe que L'appareil tardis est doté d'un écran. Vous pouvez donc hériter de l'interface ScreenRecoveryUIimplementation (consultez les instructions pour appareils sans écran) La seule fonction permettant personnaliser depuis ScreenRecoveryUI est CheckKey(), qui effectue la configuration gestion de clés asynchrones:

class TardisUI : public ScreenRecoveryUI {
  public:
    virtual KeyAction CheckKey(int key) {
        if (key == KEY_HOME) {
            return TOGGLE;
        }
        return ENQUEUE;
    }
};

Constantes KEY

Les constantes KEY_* sont définies dans linux/input.h. CheckKey() correspond à appelé peu importe ce qui se passe dans le reste de la récupération: lorsque le menu est désactivé, lorsque pendant l'installation du paquet, lors de l'effacement des données utilisateur, etc. Il peut renvoyer constantes:

  • ACTIVER/ACTIVER Activer/Désactiver l'affichage du menu et/ou du journal texte
  • REDÉMARRER : Redémarrer immédiatement l'appareil
  • IGNORER. Ignorer cette pression de touche
  • ENQUEUE. Placez cette pression de touche en file d'attente pour qu'elle soit utilisée de manière synchrone (par exemple, système de menus si l'affichage est activé)

CheckKey() est appelé à chaque fois qu'un événement d'appui vers le bas est suivi d'un événement d'action clé vers le haut pour la même clé. (La séquence d'événements A-down B-down B-up A-up ne produit que CheckKey(B) en cours d'appel.) CheckKey() peut appeler IsKeyPressed(), pour savoir si d'autres touches sont maintenues enfoncées. (Dans l'exemple ci-dessus, séquence d'événements clés, si CheckKey(B) l'a appelé IsKeyPressed(A) aurait renvoyé la valeur "true".)

CheckKey() peut conserver l'état dans sa classe ; cela peut être utile pour détecter à partir d'une séquence de touches. Cet exemple illustre une configuration légèrement plus complexe: l'affichage en maintenant le bouton Marche/Arrêt enfoncé et en appuyant sur volume+. L'appareil peut être redémarré immédiatement en appuyant sur le bouton Marche/Arrêt cinq fois de suite (sans autre touche interposée):

class TardisUI : public ScreenRecoveryUI {
  private:
    int consecutive_power_keys;

  public:
    TardisUI() : consecutive_power_keys(0) {}

    virtual KeyAction CheckKey(int key) {
        if (IsKeyPressed(KEY_POWER) && key == KEY_VOLUMEUP) {
            return TOGGLE;
        }
        if (key == KEY_POWER) {
            ++consecutive_power_keys;
            if (consecutive_power_keys >= 5) {
                return REBOOT;
            }
        } else {
            consecutive_power_keys = 0;
        }
        return ENQUEUE;
    }
};

UI de récupération d'écran

Lorsque vous utilisez vos propres images (icône d'erreur, animation d'installation, barres de progression) avec ScreenRecoveryUI, vous pouvez définir la variable animation_fps pour contrôler la vitesse dans images par seconde (FPS) des animations.

Remarque : Le script interlace-frames.py actuel vous permet de : stocker les informations animation_fps dans l'image elle-même. Dans les versions précédentes de Android, il était nécessaire de définir animation_fps vous-même.

Pour définir la variable animation_fps, remplacez la ScreenRecoveryUI::Init() dans votre sous-classe. Définissez la valeur, puis appelez la méthode parent Init() pour terminer l'initialisation. La valeur par défaut (20 FPS) correspond aux images de récupération par défaut ; lorsque vous utilisez ces images, vous n'avez pas à fournir une fonction Init(). Pour en savoir plus sur les images, consultez Images d'interface utilisateur de récupération.

Classe d'appareil

Après avoir mis en œuvre RecoveryUI, définissez la classe de votre appareil (sous-classée à partir de la classe Device intégrée). Il doit créer une instance unique de votre classe d'UI et renvoyer cette à partir de la fonction GetUI():

class TardisDevice : public Device {
  private:
    TardisUI* ui;

  public:
    TardisDevice() :
        ui(new TardisUI) {
    }

    RecoveryUI* GetUI() { return ui; }

StartRecovery

La méthode StartRecovery() est appelée au début de la récupération, une fois que l'UI a initialisé et après l'analyse des arguments, mais avant qu'une action ne soit effectuée prises. L'implémentation par défaut n'a aucun effet. Vous n'avez donc pas besoin de fournir cette information dans votre si vous n'avez rien à faire:

   void StartRecovery() {
       // ... do something tardis-specific here, if needed ....
    }

Fournissez et gérez le menu de récupération

Le système appelle deux méthodes pour obtenir la liste des lignes d'en-tête et la liste des éléments. Dans ce , elle renvoie les tableaux statiques définis en haut du fichier:

const char* const* GetMenuHeaders() { return HEADERS; }
const char* const* GetMenuItems() { return ITEMS; }

HandleMenuKey

Ensuite, fournissez une fonction HandleMenuKey(), qui accepte une pression de touche et la valeur visibilité du menu et décide de l'action à effectuer:

   int HandleMenuKey(int key, int visible) {
        if (visible) {
            switch (key) {
              case KEY_VOLUMEDOWN: return kHighlightDown;
              case KEY_VOLUMEUP:   return kHighlightUp;
              case KEY_POWER:      return kInvokeItem;
            }
        }
        return kNoAction;
    }

La méthode utilise un code de clé (qui a déjà été traité et mis en file d'attente par la CheckKey() de l'objet UI) et l'état actuel du journal de menu/texte. visibilité. La valeur renvoyée est un entier. Si la valeur est égale ou supérieure à 0, elle est considérée comme la position d'un élément de menu, qui est appelé immédiatement (voir la InvokeMenuItem() ci-dessous). Sinon, il peut s'agir de l'un des éléments suivants : constantes prédéfinies:

  • kHighlightUp Déplacer la mise en surbrillance du menu sur l'élément précédent
  • kHighlightDown : Déplacer la mise en surbrillance du menu sur l'élément suivant
  • kCallItem s'exécute. Appeler l'élément en surbrillance
  • kNoAction. Ne rien faire lorsque vous appuyez sur cette touche

Comme l'implique l'argument visible, HandleMenuKey() est appelé même si le menu est non visible. Contrairement à CheckKey(), il n'est pas appelé pendant la récupération. comme effacer des données ou installer un paquet, car cette méthode n'est appelée que lorsque la récupération est inactive. et en attente d'une entrée.

Mécanismes de trackball

Si votre appareil est équipé d'un mécanisme d'entrée de type trackball (qui génère des événements d'entrée de type EV_REL) et le code REL_Y), la récupération synthétise les appuis sur les touches KEY_UP et KEY_DOWN chaque fois que un périphérique d'entrée semblable à un trackball signale les mouvements sur l'axe Y. Tout ce que vous avez à faire est de mapper KEY_UP et KEY_DOWN a ajouté des événements aux actions du menu. Ce mappage ne se produit pas pour CheckKey(). Vous ne pouvez donc pas utiliser les mouvements du trackball comme déclencheurs pour le redémarrage ou ou seulement d'activer ou de désactiver l'affichage.

Touches de modification

Pour vérifier si les touches sont maintenues en tant que modificateurs, appelez la méthode IsKeyPressed() . de votre propre objet d'UI. Par exemple, sur certains appareils, le fait d'appuyer sur Alt+W lors d'une opération de récupération lance un l’effacement des données, que le menu soit visible ou non. Vous pourriez implémenter ceci comme suit:

   int HandleMenuKey(int key, int visible) {
        if (ui->IsKeyPressed(KEY_LEFTALT) && key == KEY_W) {
            return 2;  // position of the "wipe data" item in the menu
        }
        ...
    }

Remarque:Si visible est défini sur "false", il n'est pas logique de renvoyer la valeur des valeurs qui manipulent le menu (déplacer la mise en surbrillance, appeler l'élément en surbrillance), car l'utilisateur ne peut pas voir la mise en surbrillance. Toutefois, vous pouvez renvoyer les valeurs si vous le souhaitez.

AppelerMenuItem

Fournir ensuite une méthode InvokeMenuItem() qui mappe les positions entières dans le tableau des articles renvoyés par GetMenuItems() aux actions. Pour le tableau d'éléments exemple de tardis, utilisez:

   BuiltinAction InvokeMenuItem(int menu_position) {
        switch (menu_position) {
          case 0: return REBOOT;
          case 1: return APPLY_ADB_SIDELOAD;
          case 2: return WIPE_DATA;
          case 3: return WIPE_CACHE;
          default: return NO_ACTION;
        }
    }

Cette méthode peut renvoyer n'importe quel membre de l'énumération Protected Inaction pour indiquer au système de prendre cette (ou le membre NO_ACTION si vous souhaitez que le système ne fasse rien). C'est l'endroit où fournissent des fonctionnalités de récupération supplémentaires par rapport à ce qui se trouve dans le système: ajoutez un élément pour celui-ci dans votre menu, exécutez-le ici lorsque cet élément de menu est appelé, et renvoyez NO_ACTION pour que le système ne fait rien d'autre.

BuildinAction contient les valeurs suivantes:

  • NO_ACTION : Ne rien faire.
  • REDÉMARRER : Quittez le processus de récupération et redémarrez l'appareil normalement.
  • APP_EXT, APPLY_CACHE, APPLY_ADB_SIDELOAD. Installez un package de mise à jour depuis à différents endroits. Pour en savoir plus, consultez la section Téléchargement indépendant.
  • WIPE_CACHE : Ne reformatez que la partition du cache. Aucune confirmation n'est requise. relativement inoffensives.
  • WIPE_DATA : Reformater les données utilisateur et les partitions de cache, également appelées données d'usine réinitialisés. L'utilisateur est invité à confirmer cette action avant de continuer.

La dernière méthode, WipeData(), est facultative et est appelée chaque fois qu'un effacement de données est lancée (soit à partir du menu de récupération, soit lorsque l'utilisateur a choisi d'effectuer une par défaut à partir du système principal). Cette méthode est appelée avant que les données utilisateur les partitions sont effacées. Si votre appareil stocke des données utilisateur ailleurs que dans ces deux des partitions, vous devez l'effacer ici. Vous devez renvoyer 0 pour indiquer la réussite et une autre en cas d'échec. Toutefois, la valeur renvoyée est actuellement ignorée. Données utilisateur et cache les partitions sont effacées, que vous rencontriez un succès ou un échec.

   int WipeData() {
       // ... do something tardis-specific here, if needed ....
       return 0;
    }

Marque-appareil

Enfin, ajoutez du code récurrent à la fin du fichier recovery_ui.cpp pour la fonction make_device() qui crée et renvoie une instance de votre classe Device:

class TardisDevice : public Device {
   // ... all the above methods ...
};

Device* make_device() {
    return new TardisDevice();
}

Une fois le fichier recovery_ui.cpp créé, créez-le et associez-le à la récupération sur votre appareil. Dans Android.mk, créez une bibliothèque statique contenant uniquement ce fichier C++:

device/yoyodyne/tardis/recovery/Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

LOCAL_MODULE_TAGS := eng
LOCAL_C_INCLUDES += bootable/recovery
LOCAL_SRC_FILES := recovery_ui.cpp

# should match TARGET_RECOVERY_UI_LIB set in BoardConfig.mk
LOCAL_MODULE := librecovery_ui_tardis

include $(BUILD_STATIC_LIBRARY)

Ensuite, dans la configuration de la carte pour cet appareil, spécifiez votre bibliothèque statique en tant que valeur "TARGET_RECOVERY_UI_LIB".

device/yoyodyne/tardis/BoardConfig.mk
 [...]

# device-specific extensions to the recovery UI
TARGET_RECOVERY_UI_LIB := librecovery_ui_tardis

Images de récupération de l'interface utilisateur

L'interface utilisateur de récupération se compose d'images. Idéalement, les utilisateurs n'interagissent jamais avec l'interface utilisateur: Lors d'une mise à jour normale, le téléphone démarre en mode récupération, remplit la barre de progression de l'installation, et démarre dans le nouveau système sans intervention de l'utilisateur. Si un système le problème de mise à jour, la seule action que l’utilisateur peut prendre est d’appeler le service client.

Une interface basée uniquement sur des images évite d'avoir à les localiser. Cependant, à partir d'Android 5.0, de mise à jour peut afficher une chaîne de texte (par exemple, "Installation de la mise à jour du système...") avec l'image. Pour en savoir plus, consultez l'article Texte de récupération localisé.

Android 5.0 et versions ultérieures

L'interface utilisateur de récupération d'Android 5.0 ou version ultérieure utilise deux images principales: l'image error et l'animation d'installation.

image affichée lors d&#39;une erreur OTA

Figure 1 : icon_error.png

Image affichée lors de l&#39;installation d&#39;OTA

Figure 2 : icon_installed.png

L'animation d'installation est représentée par une seule image PNG avec différentes images de la animation entrelacée par ligne (c'est pourquoi la figure 2 semble écrasée). Par exemple, pour un Une animation de 200 x 200 sur sept cadres, qui permet de créer une seule image de 200 x 1 400 où la première image correspond aux lignes 0, 7, 14, 21, ...; le deuxième frame correspond aux lignes 1, 8, 15, 22, ...; etc. L'image combinée inclut bloc de texte qui indique le nombre d'images d'animation et le nombre d'images par seconde (FPS). L'outil bootable/recovery/interlace-frames.py prend un ensemble d'images d'entrée et les combine dans l'image composite nécessaire à la récupération.

Les images par défaut sont disponibles dans différentes densités et se trouvent dans bootable/recovery/res-$DENSITY/images (par exemple, bootable/recovery/res-hdpi/images). Pour utiliser une image statique lors de l'installation, il vous suffit de fournir l'image icon_installed.png et de définir le nombre d'images dans la l'animation sur 0 (l'icône d'erreur n'est pas animée, il s'agit toujours d'une image statique).

Android 4.x et versions antérieures

L'interface utilisateur de récupération pour Android 4.x ou version antérieure utilise l'image d'erreur (illustrée ci-dessus) et l' installation et plusieurs images en superposition:

Image affichée lors de l&#39;installation d&#39;OTA

Figure 3 : icon_installed.png

image affichée en premier
superposition

Figure 4.icon-installed_overlay01.png

image affichée en septième
superposition

Figure 5 : icon_installed_overlay07.png

Pendant l'installation, l'affichage à l'écran est construit en dessinant le puis en dessinant l'un des cadres superposés au-dessus avec le décalage approprié. Ici, un indicateur est superposée pour indiquer où la superposition est placée sur l'image de base:

image composite de
superposition &quot;Installation + 1&quot;

Figure 6. Installation de l'image d'animation 1 (icon_installed.png + icon_installed_overlay01.png)

image composite de
superposition &quot;installation plus septième&quot;

Figure 7. Installation du cadre d'animation 7 (icon_installed.png + icon_installed_overlay07.png)

Les images suivantes s'affichent en dessinant uniquement l'image de superposition suivante au-dessus déjà présentes ; l'image de base n'est pas redessinée.

Le nombre d'images dans l'animation, la vitesse souhaitée et les décalages X et Y de la superposition par rapport à la base sont définies par les variables de membre de la classe ScreenRecoveryUI. Lorsque vous utilisez personnalisées au lieu des images par défaut, remplacez la méthode Init() dans votre afin de modifier ces valeurs pour vos images personnalisées (pour en savoir plus, consultez ScreenRecoveryUI). Le script bootable/recovery/make-overlay.py peut vous aider à convertir un ensemble de cadres d'image. à "image de base + images de superposition" nécessaire à la reprise, y compris le calcul du les décalages nécessaires.

Les images par défaut se trouvent dans bootable/recovery/res/images. Utiliser une image statique lors de l'installation, il vous suffit de fournir l'image icon_installed.png et de définir le nombre de d'images de l'animation sur 0 (l'icône d'erreur n'est pas animée, il s'agit toujours d'une image statique).

Texte de récupération localisé

Android 5.x affiche une chaîne de texte (par exemple, "Installation de la mise à jour du système..."), ainsi que l'image. Lorsque le système principal démarre lors du processus de récupération, les paramètres régionaux actuels de l'utilisateur sont transmis en tant que de ligne de commande de récupération. Pour chaque message à afficher, la récupération inclut une seconde une image composite avec des chaînes de texte prérendues pour ce message dans chaque langue.

Exemple d'image de chaînes de texte de récupération:

image du texte de récupération

Figure 8. Texte localisé pour les messages de récupération

Le texte de récupération peut afficher les messages suivants:

  • Installation de la mise à jour du système...
  • Erreur !
  • Effacement... (lors de l'effacement des données ou du rétablissement de la configuration d'usine)
  • Aucune commande (lorsqu'un utilisateur démarre la récupération manuellement)

L'application Android dans bootable/recovery/tools/recovery_l10n/ affiche les localisations d'un message et crée l'image composite. Pour en savoir plus sur l'utilisation de cette application, consultez les commentaires dans bootable/recovery/tools/recovery_l10n/src/com/android/recovery_l10n/Main.java

Lorsqu'un utilisateur démarre manuellement le processus de récupération, il est possible que les paramètres régionaux ne soient pas disponibles et qu'aucun texte ne soit affiché. Ne rendez pas ces messages essentiels au processus de récupération.

Remarque:Il s'agit de l'interface masquée qui affiche les messages de journal et permet à l'utilisateur de l'option "Sélectionner des actions dans le menu" n'est disponible qu'en anglais.

Barres de progression

Des barres de progression peuvent apparaître sous l'image (ou l'animation) principale. La barre de progression est créée par combiner deux images d'entrée, qui doivent avoir la même taille:

barre de progression vide

Figure 9 : progress_empty.png

barre de progression complète

Figure 10 : progress_fill.png

L'extrémité gauche de l'image de remplissage s'affiche à côté de l'extrémité droite de l'image une image vide pour afficher la barre de progression. La position de la frontière entre les deux les images sont modifiées pour indiquer la progression. Par exemple, avec les paires d'images d'entrée ci-dessus, display:

barre de progression à 1%

Figure 11 : Barre de progression à 1 %>

barre de progression à 10%

Figure 12. Barre de progression à 10%

barre de progression à 50%

Figure 13. Barre de progression à 50%

Vous pouvez fournir des versions spécifiques à l'appareil de ces images en les plaçant dans (dans ce exemple) device/yoyodyne/tardis/recovery/res/images pour en savoir plus. Les noms de fichiers doivent correspondre à ceux indiqués ci-dessus. lorsqu'un fichier est trouvé dans ce répertoire, de compilation l'utilise plutôt que l'image par défaut correspondante. Seuls les fichiers PNG en RVB ou Le format RVBA avec une profondeur de couleur de 8 bits est compatible.

Remarque:Sous Android 5.x, si les paramètres régionaux sont connus pour permettre la récupération et qu'il s'agit d'un qui se lit de droite à gauche (arabe, hébreu, etc.), la barre de progression se remplit de droite à à gauche.

Appareils sans écran

Tous les appareils Android ne sont pas équipés d'un écran. Si votre appareil est un appareil headless ou si audio uniquement, vous devrez peut-être personnaliser davantage l'interface utilisateur de récupération. À la place de la création d'une sous-classe de ScreenRecoveryUI, sous-classez directement sa classe parente RecoveryUI.

RecoveryUI dispose de méthodes pour gérer les opérations d'interface utilisateur de niveau inférieur, telles que "activer l'affichage", « mettre à jour la barre de progression », « montre le menu », « changer la sélection du menu », Vous pouvez remplacer ceux-ci pour fournir une interface appropriée à votre appareil. Peut-être que votre appareil a des voyants où vous pouvez utiliser différentes couleurs ou motifs de clignotement pour indiquer l'état, ou peut-être pouvez-vous jouer audio. Si vous ne souhaitez pas du tout utiliser un menu ou le mode d'affichage du texte, empêchez l'accès à ces éléments avec CheckKey() et Implémentations HandleMenuKey() qui n'activent jamais l'affichage ni ne sélectionnent de menu élément. Dans ce cas, la plupart des méthodes RecoveryUI que vous devez fournir peuvent simplement être vides bouchons.)

Consultez bootable/recovery/ui.h pour connaître les méthodes de déclaration de RecoveryUI que vous devez prendre en charge. RecoveryUI est abstrait : certaines méthodes sont purement virtuelles et doivent être fournies par mais il contient le code permettant de traiter les entrées de clé. Vous pouvez ignorer si votre appareil ne possède pas de clés ou si vous souhaitez les traiter différemment.

Mise à jour

Vous pouvez utiliser du code spécifique à l'appareil dans l'installation du package de mise à jour en fournissant votre propres fonctions d'extension pouvant être appelées depuis votre script de mise à jour. Voici un exemple pour l'appareil tardis:

device/yoyodyne/tardis/recovery/recovery_updater.c
#include <stdlib.h>
#include <string.h>

#include "edify/expr.h"

Chaque fonction d'extension a la même signature. Les arguments sont le nom par lequel a été appelée, un cookie State*, le nombre d'arguments entrants Tableau de pointeurs Expr* représentant les arguments. La valeur renvoyée est Value* nouvellement alloués.

Value* ReprogramTardisFn(const char* name, State* state, int argc, Expr* argv[]) {
    if (argc != 2) {
        return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc);
    }

Vos arguments n'ont pas été évalués au moment où votre fonction est appelée. détermine lesquels d'entre eux sont évalués et combien de fois. Vous pouvez donc utiliser les extensions pour implémenter vos propres structures de contrôle. Call Evaluate() pour évaluer un argument Expr* , en renvoyant une Value*. Si Evaluate() renvoie NULL, vous devez libérer toutes les ressources que vous conservez et renvoyer immédiatement NULL (ce propage l'abandon de la pile edify). Dans le cas contraire, vous devenez propriétaire de la Valeur renvoyée et sont responsables de l'appel FreeValue().

Supposons que la fonction ait besoin de deux arguments: une clé de valeur de chaîne et une valeur de blob image : Vous pouvez lire des arguments comme ceux-ci:

   Value* key = EvaluateValue(state, argv[0]);
    if (key == NULL) {
        return NULL;
    }
    if (key->type != VAL_STRING) {
        ErrorAbort(state, "first arg to %s() must be string", name);
        FreeValue(key);
        return NULL;
    }
    Value* image = EvaluateValue(state, argv[1]);
    if (image == NULL) {
        FreeValue(key);    // must always free Value objects
        return NULL;
    }
    if (image->type != VAL_BLOB) {
        ErrorAbort(state, "second arg to %s() must be blob", name);
        FreeValue(key);
        FreeValue(image)
        return NULL;
    }

Vérifier les valeurs NULL et libérer des arguments précédemment évalués peut s'avérer fastidieux pour plusieurs . La fonction ReadValueArgs() peut faciliter cette opération. Au lieu du code ci-dessus, vous auriez pu écrire ceci:

   Value* key;
    Value* image;
    if (ReadValueArgs(state, argv, 2, &key, &image) != 0) {
        return NULL;     // ReadValueArgs() will have set the error message
    }
    if (key->type != VAL_STRING || image->type != VAL_BLOB) {
        ErrorAbort(state, "arguments to %s() have wrong type", name);
        FreeValue(key);
        FreeValue(image)
        return NULL;
    }

ReadValueArgs() ne fait pas de vérification du type. Vous devez donc le faire ici. c'est plus pratique de le faire avec une seule instruction if, au détriment de la réduction un message d'erreur spécifique en cas d'échec. Mais ReadValueArgs() gère l'évaluation chaque argument et en libérant tous les arguments précédemment évalués (en plus de définir une valeur message d'erreur) si l'une des évaluations échoue. Vous pouvez utiliser un Fonction de commodité ReadValueVarArgs() pour évaluer un nombre variable de (la fonction renvoie un tableau de Value*).

Après avoir évalué les arguments, effectuez le travail de la fonction:

   // key->data is a NUL-terminated string
    // image->data and image->size define a block of binary data
    //
    // ... some device-specific magic here to
    // reprogram the tardis using those two values ...

La valeur renvoyée doit être un objet Value*. la propriété de cet objet sera transmise à l'appelant. L'appelant s'approprie toutes les données auxquelles renvoie cette Value* : plus précisément, le membre de données.

Dans ce cas, vous souhaitez renvoyer une valeur "true" ou "false" pour indiquer la réussite de l'opération. N'oubliez pas convention selon laquelle la chaîne vide est false et toutes les autres chaînes sont true. Toi doit malloc un objet Valeur avec une copie malloc de la chaîne constante à renvoyer, car le l'appelant free() les deux. N'oubliez pas d'appeler FreeValue() au que vous obtenez en évaluant vos arguments !

   FreeValue(key);
    FreeValue(image);

    Value* result = malloc(sizeof(Value));
    result->type = VAL_STRING;
    result->data = strdup(successful ? "t" : "");
    result->size = strlen(result->data);
    return result;
}

La fonction de commodité StringValue() encapsule une chaîne dans un nouvel objet Value. Utilisez pour écrire le code ci-dessus de manière plus succincte:

   FreeValue(key);
    FreeValue(image);

    return StringValue(strdup(successful ? "t" : ""));
}

Pour accrocher des fonctions à l'interpréteur edify, fournissez la fonction Register_foo, où foo est le nom de la bibliothèque statique contenant ce code. Appelez RegisterFunction() pour enregistrer chaque fonction d'extension. Par nommage de fonctions spécifiques à l'appareil device.whatever pour éviter des conflits avec de futures fonctions intégrées ajoutées.

void Register_librecovery_updater_tardis() {
    RegisterFunction("tardis.reprogram", ReprogramTardisFn);
}

Vous pouvez maintenant configurer le fichier makefile pour créer une bibliothèque statique avec votre code. (Cette opération est la même makefile utilisé pour personnaliser l'UI de récupération dans la section précédente. votre appareil peut avoir à la fois bibliothèques statiques définies ici.)

device/yoyodyne/tardis/recovery/Android.mk
include $(CLEAR_VARS)
LOCAL_SRC_FILES := recovery_updater.c
LOCAL_C_INCLUDES += bootable/recovery

Le nom de la bibliothèque statique doit correspondre au nom de la fonction Register_libname qu'elle contient.

LOCAL_MODULE := librecovery_updater_tardis
include $(BUILD_STATIC_LIBRARY)

Enfin, configurez le build de récupération pour récupérer votre bibliothèque. Ajouter votre bibliothèque à TARGET_RECOVERY_UPDATER_LIBS (qui peut contenir plusieurs bibliothèques ; elles sont toutes enregistrées). Si votre code dépend d'autres bibliothèques statiques qui ne sont pas elles-mêmes des extensions Edify (par exemple, s'ils n'ont pas de fonction Register_libname), vous pouvez les lister TARGET_RECOVERY_UPDATER_EXTRA_LIBS pour les associer à l'outil de mise à jour sans appeler leur (inexistante). Par exemple, si le code spécifique à l'appareil souhaite utiliser zlib pour décompresser les données, vous devez inclure libz ici.

device/yoyodyne/tardis/BoardConfig.mk
 [...]

# add device-specific extensions to the updater binary
TARGET_RECOVERY_UPDATER_LIBS += librecovery_updater_tardis
TARGET_RECOVERY_UPDATER_EXTRA_LIBS +=

Les scripts de mise à jour de votre package OTA peuvent désormais appeler votre fonction comme n'importe quel autre. Pour reprogrammer votre appareil tardis, le script de mise à jour peut contenir les éléments suivants: tardis.reprogram("the-key", package_extract_file("tardis-image.dat")) Ce mode utilise la version à argument unique de la fonction intégrée package_extract_file() ; qui renvoie le contenu d'un fichier extrait du package de mise à jour sous forme de blob pour produire le deuxième argument de la nouvelle fonction d'extension.

Génération de packages OTA

Le dernier élément consiste à faire en sorte que les outils de génération de packages OTA connaissent des données spécifiques à l'appareil et émettre des scripts de mise à jour qui incluent des appels vers vos fonctions d'extension.

Tout d'abord, demandez au système de compilation de connaître un blob de données spécifique à un appareil. Prendre en compte vos données fichier se trouve dans device/yoyodyne/tardis/tardis.dat, déclarez ce qui suit dans votre AndroidBoard.mk de l'appareil:

device/yoyodyne/tardis/AndroidBoard.mk
  [...]

$(call add-radio-file,tardis.dat)

Vous pouvez aussi le placer dans un fichier Android.mk, mais il doit être protégé par un appareil car tous les fichiers Android.mk de l'arborescence sont chargés, quel que soit l'appareil utilisé conçue. (Si votre arborescence comprend plusieurs appareils, seul le fichier tardis.dat doit être ajouté au moment la construction de l'appareil tardis.)

device/yoyodyne/tardis/Android.mk
  [...]

# an alternative to specifying it in AndroidBoard.mk
ifeq (($TARGET_DEVICE),tardis)
  $(call add-radio-file,tardis.dat)
endif

Ils sont appelés fichiers radio pour des raisons historiques ; ils n'ont peut-être rien à voir le signal radio de l'appareil (le cas échéant). Il s'agit simplement de blobs opaques de données que le système de compilation copie le fichier .zip des fichiers cibles utilisé par les outils de génération OTA. Lorsque vous créez une compilation, tardis.dat est stocké dans le fichier target-files.zip sous le nom RADIO/tardis.dat. Vous pouvez appeler add-radio-file plusieurs fois pour ajouter autant de fichiers que vous le souhaitez.

Module Python

Pour étendre les outils de publication, écrivez un module Python (doit être nommé releasetools.py). Les outils pouvez appeler si elle est présente. Exemple :

device/yoyodyne/tardis/releasetools.py
import common

def FullOTA_InstallEnd(info):
  # copy the data into the package.
  tardis_dat = info.input_zip.read("RADIO/tardis.dat")
  common.ZipWriteStr(info.output_zip, "tardis.dat", tardis_dat)

  # emit the script code to install this data on the device
  info.script.AppendExtra(
      """tardis.reprogram("the-key", package_extract_file("tardis.dat"));""")

Une fonction distincte gère le cas de génération d'un package OTA incrémentiel. Pour cette Par exemple, supposons que vous deviez reprogrammer le tardis uniquement lorsque le fichier tardis.dat a été modifié. entre deux compilations.

def IncrementalOTA_InstallEnd(info):
  # copy the data into the package.
  source_tardis_dat = info.source_zip.read("RADIO/tardis.dat")
  target_tardis_dat = info.target_zip.read("RADIO/tardis.dat")

  if source_tardis_dat == target_tardis_dat:
      # tardis.dat is unchanged from previous build; no
      # need to reprogram it
      return

  # include the new tardis.dat in the OTA package
  common.ZipWriteStr(info.output_zip, "tardis.dat", target_tardis_dat)

  # emit the script code to install this data on the device
  info.script.AppendExtra(
      """tardis.reprogram("the-key", package_extract_file("tardis.dat"));""")

Fonctions du module

Vous pouvez fournir les fonctions suivantes dans le module (n'implémentez que celles dont vous avez besoin).

FullOTA_Assertions()
Appelée peu avant le début de la génération d'une OTA complète. C'est un bon endroit pour émettre des assertions sur l'état actuel de l'appareil. N'émettez pas de commandes de script pour modifier le appareil.
FullOTA_InstallBegin()
Appelé après la réussite de toutes les assertions concernant l'état de l'appareil, mais avant toute modification ont été faites. Vous pouvez émettre des commandes pour les mises à jour spécifiques à l'appareil qui doivent s'exécuter avant tout autre élément sur l'appareil a été modifié.
FullOTA_InstallEnd()
Appelé à la fin de la génération du script, après les commandes de script, pour mettre à jour le démarrage et partitions système ont été émises. Vous pouvez également émettre des commandes supplémentaires pour des mises à jour spécifiques à chaque appareil.
IncrementalOTA_Assertions()
Semblable à FullOTA_Assertions(), mais appelé lors de la génération d'une "update".
IncrementalOTA_VerifyBegin()
Appelé après la réussite de toutes les assertions concernant l'état de l'appareil, mais avant qu'aucune modification n'ait été effectuée n'a pas été prise en compte. Vous pouvez émettre des commandes pour les mises à jour spécifiques à l'appareil qui doivent s'exécuter avant toute autre chose sur l'appareil a été modifiée.
IncrementalOTA_VerifyEnd()
Appelé à la fin de la phase de vérification, lorsque le script a confirmé la les fichiers qu’il va toucher ont le contenu de départ attendu. À ce stade, rien sur l'appareil a été modifié. Vous pouvez également émettre du code pour des spécifications supplémentaires validations.
IncrementalOTA_InstallBegin()
Appelé après que les fichiers à corriger ont été vérifiés comme ayant les performances attendues l'état before, mais avant toute modification. Vous pouvez émettre des commandes les mises à jour spécifiques à l'appareil qui doivent s'exécuter avant toute modification apportée à l'appareil.
IncrementalOTA_InstallEnd()
Comme pour le package OTA complet, cette méthode est appelée à la fin du script. génération, une fois que les commandes de script de mise à jour des partitions système et de démarrage ont été émises. Vous pouvez également émettre des commandes supplémentaires pour les mises à jour spécifiques à l'appareil.

Remarque:Si l'appareil n'est plus alimenté, l'installation OTA peut redémarrer à partir du à partir de zéro. Soyez prêt à faire face à des appareils sur lesquels ces commandes ont déjà été exécutées, entièrement ou partiellement.

Transmettre des fonctions à des objets info

Transmettez des fonctions à un seul objet d'informations qui contient divers éléments utiles:

  • info.input_zip. (Agences OTA complètes uniquement) L'objet zipfile.ZipFile pour la d'entrée target-files .zip.
  • info.source_zip. (Agences OTA incrémentielles uniquement) L'objet zipfile.ZipFile pour le fichier .zip source target-files (compilation déjà présente sur le périphérique lorsque le package incrémentiel) est en cours d'installation).
  • info.target_zip. (Agences OTA incrémentielles uniquement) L'objet zipfile.ZipFile pour le fichier ZIP cible des fichiers cibles (la compilation que le package incrémentiel place sur l'appareil) ;
  • info.output_zip. Création du package en cours un objet zipfile.ZipFile ouvert pour écrire. Utilisez common.ZipWriteStr(info.output_zip, filename, data) pour ajouter un dans le package.
  • info.script Objet de script auquel vous pouvez ajouter des commandes. Appeler info.script.AppendExtra(script_text) pour générer du texte dans le script. Assurez-vous que le texte de sortie se termine par un point-virgule afin de ne pas émettre de commandes. par la suite.

Pour en savoir plus sur l'objet "info", consultez Documentation Python Software Foundation pour les archives ZIP

Spécifier l'emplacement du module

Spécifiez l'emplacement du script releasetools.py de votre appareil dans le fichier BoardConfig.mk:

device/yoyodyne/tardis/BoardConfig.mk
 [...]

TARGET_RELEASETOOLS_EXTENSIONS := device/yoyodyne/tardis

Si TARGET_ReleaseTOOLS_EXTENSIONS n'est pas défini, la valeur par défaut est la Annuaire $(TARGET_DEVICE_DIR)/../common (device/yoyodyne/common ) dans cet exemple). Il est préférable de définir explicitement l'emplacement du script releasetools.py. Lors de la compilation de l'appareil tardis, le script releasetools.py est inclus dans les fichiers cibles Fichier .zip (META/releasetools.py ).

Lorsque vous exécutez les outils de publication (img_from_target_files ou ota_from_target_files), le script releasetools.py dans le fichier .zip target-files, si présente, est préférable à celui de l'arborescence source Android. Vous pouvez aussi explicitement spécifiez le chemin d'accès aux extensions spécifiques à l'appareil avec -s (ou --device_specific), qui est prioritaire. Vous pouvez ainsi corriger les erreurs et apporter des modifications aux extensions releasetools, puis appliquer ces modifications aux anciennes "target-files".

Désormais, lorsque vous exécutez ota_from_target_files, il récupère automatiquement module spécifique à l'appareil à partir du fichier ZIP target_files et l'utilise lors de la génération de l'OTA packages:

./build/make/tools/releasetools/ota_from_target_files \
    -i PREVIOUS-tardis-target_files.zip \
    dist_output/tardis-target_files.zip \
    incremental_ota_update.zip

Vous pouvez également spécifier des extensions spécifiques à l'appareil lorsque vous exécutez ota_from_target_files

./build/make/tools/releasetools/ota_from_target_files \
    -s device/yoyodyne/tardis \
    -i PREVIOUS-tardis-target_files.zip \
    dist_output/tardis-target_files.zip \
    incremental_ota_update.zip

Remarque:Pour obtenir la liste complète des options, consultez le ota_from_target_files commentaires dans build/make/tools/releasetools/ota_from_target_files

Mécanisme de téléchargement indépendant

La récupération dispose d'un mécanisme de téléchargement indépendant qui permet d'installer manuellement un package de mise à jour sans en le téléchargeant Over The Air par le système principal. Le téléchargement indépendant est utile pour déboguer les modifications sur les appareils où le système principal ne peut pas être démarré.

Jusqu'à présent, le téléchargement indépendant se produisait en chargeant des paquets à partir de la carte SD de l'appareil. dans sur un appareil non amorçable, le package peut être placé sur la carte SD en utilisant un autre un ordinateur, puis la carte SD insérée dans l'appareil. Pour prendre en charge les appareils Android sans mémoire de stockage externe amovible, la récupération est compatible avec deux mécanismes supplémentaires de téléchargement indépendant: charger des packages à partir de la partition de cache et les charger via USB à l'aide d'adb.

Pour appeler chaque mécanisme de téléchargement indépendant, la méthode Device::InvokeMenuItem() de votre appareil peut renvoyer les valeurs suivantes de builtinAction:

  • APPLIQUEZ_EXT. Télécharger un package de mise à jour indépendamment depuis un espace de stockage externe ( /sdcard) du répertoire d'utilisateurs). Le fichier recovery.fstab doit définir le point d'installation /sdcard . C'est ne peut pas être utilisée sur les appareils qui émulent une carte SD avec un lien symbolique vers /data (ou certains mécanisme similaire). /data n'est généralement pas disponible pour la récupération, car il peuvent être chiffrées. L'UI de récupération affiche un menu de fichiers ZIP dans /sdcard et permet à l'utilisateur d'en sélectionner une.
  • APPLIQUER_CACHE : Semblable au chargement d'un package à partir de /sdcard, sauf que Le répertoire /cache (qui est toujours disponible pour la récupération) est utilisé. à la place. À partir du système standard, /cache n'est accessible en écriture que par les utilisateurs privilégiés, Si l'appareil n'est pas amorçable, le répertoire /cache ne peut pas être écrit dans (ce qui rend ce mécanisme d'utilité limitée).
  • APPLIQUER_ADB_SIDELOAD. Permet à l'utilisateur d'envoyer un colis à l'appareil via un câble USB et l'outil de développement adb. Lorsque ce mécanisme est appelé, la récupération démarre du daemon adbd pour permettre à adb sur un ordinateur hôte connecté de communiquer avec lui. Ce mini n'accepte qu'une seule commande: adb sideload filename. Le fichier nommé est envoyé de la machine hôte à l'appareil, qui vérifie et l'installe comme s'il était stocké en local.

Quelques mises en garde:

  • Seul le transport USB est pris en charge.
  • Si votre récupération exécute adbd normalement (généralement vrai pour userdebug et eng builds), cela sera arrêté lorsque l'appareil est en mode de chargement indépendant adb et redémarrera lorsqu'adb le téléchargement indépendant a fini de recevoir un package. En mode de chargement indépendant adb, aucune commande adb ne permet d'effectuer d'autres de sideload professionnels ( logcat, reboot, push, pull , shell, etc. échouent toutes).
  • Vous ne pouvez pas quitter le mode de chargement indépendant adb sur l'appareil. Pour annuler, vous pouvez envoyer /dev/null (ou tout autre élément qui n'est pas un package valide) comme package, et l'appareil ne le validera pas et arrêtera la procédure d'installation. RecoveryUI la méthode CheckKey() de l'implémentation continue d'être appelée pour les pressions de touche, Vous pouvez ainsi fournir une séquence de touches qui redémarre l'appareil et fonctionne en mode de chargement indépendant adb.