Código específico para el dispositivo

El sistema de recuperación incluye varios hooks para insertar código específico del dispositivo para que la las actualizaciones también pueden actualizar partes del dispositivo que no sean el sistema Android (p.ej., la banda base o procesador de radio).

En las siguientes secciones y ejemplos, se personaliza el dispositivo tardis que produce el yoyodyne.

Mapa de particiones

A partir de Android 2.3, la plataforma admite dispositivos flash eMMc y el sistema de archivos ext4 que se ejecuta. en esos dispositivos. También es compatible con los dispositivos de tecnología de memoria (MTD) y los dispositivos un sistema de archivos de versiones anteriores.

TARGET_RECOVERY_FSTAB especifica el archivo de mapa de partición; a este archivo lo usan tanto el objeto binario de recuperación y las herramientas de compilación de paquetes. Puedes especificar el nombre del archivo de mapa en TARGET_RECOVERY_FSTAB en BoardConfig.mk.

Un archivo de mapa de partición de muestra podría verse de la siguiente manera:

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

A excepción de /sdcard, que es opcional, todos los puntos de activación en este Debe definirse este ejemplo (los dispositivos también pueden agregar particiones adicionales). Existen cinco tipos de apps compatibles tipos de sistemas de archivos:

jarabe2
Un sistema de archivos yaffs2 sobre un dispositivo flash MTD. "dispositivo" debe ser el nombre de la partición de MTD y debe aparecer en /proc/mtd.
Mtd
Una partición de MTD sin procesar, que se usa para particiones que se pueden iniciar, como el inicio y la recuperación. El MTD no es pero el punto de activación se usa como clave para ubicar la partición. "dispositivo" Debe ser el nombre de la partición de MTD en /proc/mtd.
ext4
Un sistema de archivos ext4 sobre un dispositivo flash eMMc "dispositivo" debe ser la ruta del dispositivo de bloques.
emmc
Un dispositivo de bloques eMMc sin procesar, que se usa para particiones que se pueden iniciar, como el inicio y la recuperación. Similar a el tipo mtd, la eMMc nunca se activa, pero la cadena de punto de activación se usa para ubicar el dispositivo en la tabla.
Vfat
Un sistema de archivos FAT sobre un dispositivo de almacenamiento en bloques, generalmente para almacenamiento externo, como una tarjeta SD. El “device” es el dispositivo de almacenamiento en bloques; device2 es un segundo dispositivo de almacenamiento en bloques que el sistema intenta activar si falla la instalación del dispositivo principal (por motivos de compatibilidad con las tarjetas SD, que pueden o no ser formateada con una tabla de particiones).

Todas las particiones se deben activar en el directorio raíz (es decir, el valor del punto de activación debe comienzan con una barra y no tienen otras barras). Esta restricción solo se aplica a la activación sistemas de archivos en recuperación; el sistema principal es libre de montarlas en cualquier lugar. Los directorios /boot, /recovery y /misc deben ser tipos sin procesar (mtd o emmc), mientras que los directorios /system, /data, /cache y /sdcard (si está disponible) deben ser tipos de sistema de archivos. (yaffs2, ext4 o vfat).

A partir de Android 3.0, el archivo recovery.fstab obtiene un campo opcional adicional, options. Actualmente, la única opción definida es length , que te permite explícitamente especificar la longitud de la partición. Esta longitud se usa cuando se cambia el formato de la partición (p.ej., para la partición de datos del usuario durante una operación de limpieza o restablecimiento de la configuración de fábrica, o una partición del sistema durante la instalación de un paquete inalámbrico completo). Si el valor de la longitud es negativo, Luego, se toma el tamaño al formatear cuando se agrega el valor de longitud al tamaño verdadero de la partición. Para instancia, con la configuración “length=-16384” significa que los últimos 16 K de esa partición no se se sobrescribe cuando se vuelve a formatear esa partición. Esto admite funciones como la encriptación de la partición userdata (donde los metadatos de encriptación se almacenan al final de la partición que no debe sobrescribirse).

Nota: Los campos device2 y options son opcionales, lo que crea la ambigüedad en el análisis. Si la entrada en el cuarto campo de la línea comienza con una “/” se considera una entrada de device2 ; si la entrada no comienza con "/" se considera un campo de options.

Animación de inicio

Los fabricantes de dispositivos pueden personalizar la animación que se muestra cuando se usa un dispositivo Android. se está iniciando. Para ello, crea un archivo ZIP organizado y ubicado de acuerdo con las más recientes en formato bootanimation.

Para dispositivos Android Things, puedes cargar el archivo comprimido en la consola de Android Things para incluir las imágenes en el producto seleccionado.

Nota: Estas imágenes deben cumplir con los lineamientos de desarrollo de la marca de Android. Para los lineamientos de desarrollo de la marca, consulta la sección de Android de la Marketing del socio Hub

IU de recuperación

Para admitir dispositivos con diferentes hardware disponibles (botones físicos, LED, pantallas, etc.), puedes personalizar la interfaz de recuperación para mostrar el estado y acceder a funciones ocultas para cada dispositivo.

Tu objetivo es compilar una pequeña biblioteca estática con un par de objetos C++ para proporcionar la funciones específicas del dispositivo. El archivo bootable/recovery/default_device.cpp se usa de forma predeterminada y constituye un punto de partida para copiar cuando escribas una versión de este archivo para tu dispositivo.

Nota: Es posible que veas un mensaje que diga No Command aquí. Activar o desactivar texto, mantén presionado el botón de encendido mientras lo presionas para subir el volumen. Si tus dispositivos no tienen ambos botones, mantén presionado cualquier botón para alternar el texto.

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

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

Funciones de encabezado y elementos

La clase Device requiere funciones para mostrar encabezados y elementos que aparecen en la menú de recuperación. Los encabezados describen cómo operar el menú (es decir, los controles para cambiar o seleccionar el elemento destacado).

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 };

Nota: Las líneas largas se truncan (no se ajustan), por lo que debes conservar el ancho de la la pantalla del dispositivo.

Personalizar CheckKey

A continuación, define la implementación de RecoveryUI de tu dispositivo. En este ejemplo, se supone que El dispositivo tardis tiene una pantalla, por lo que puedes heredar de la Implementación de ScreenRecoveryUI (consulta las instrucciones para dispositivos sin pantalla). La única función para el usuario de ScreenRecoveryUI es CheckKey(), que hace la ejecución manejo de claves asíncronos:

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

Constantes de KEY

Las constantes KEY_* se definen en linux/input.h. CheckKey() es sin importar lo que ocurra en el resto de la recuperación: cuando el menú está desactivado, cuando está encendido, durante la instalación del paquete, durante la limpieza de userdata, etc. Puede mostrar uno de cuatro constantes:

  • ACTIVAR Activar o desactivar la visualización del menú o el inicio de sesión de texto
  • REINICIAR Reiniciar el dispositivo de inmediato
  • IGNORAR Ignorar esta pulsación de teclas
  • ENQUEUE. Pon esta pulsación de teclas en cola para que la consuma de forma síncrona (es decir, por el el sistema del menú si la pantalla está habilitada)

Se llama a CheckKey() cada vez que un evento de tecla presionada sigue a un evento de tecla presionada para la misma clave. (La secuencia de eventos A-down B-up-A-up da como resultado solo en CheckKey(B)). CheckKey() puede llamar IsKeyPressed(), para averiguar si se están presionando otras teclas (En el ejemplo anterior, de eventos clave, si CheckKey(B) llamó a IsKeyPressed(A), habría resultado verdadero).

CheckKey() puede mantener el estado en su clase. esto puede ser útil para detectar secuencias de teclas. En este ejemplo, se muestra una configuración un poco más compleja: la pantalla se activa o desactiva manteniendo presionado el botón de encendido y presionando el botón para subir el volumen, y el dispositivo se puede reiniciar de inmediato si Presiona el botón de encendido cinco veces seguidas (sin otras teclas):

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;
    }
};

IU de recuperación de pantalla

Cuando uses tus propias imágenes (ícono de error, animación de instalación, barras de progreso) con ScreenRecoveryUI, puedes establecer la variable animation_fps para controlar la velocidad en fotogramas por segundo (FPS) de animaciones.

Nota: La secuencia de comandos interlace-frames.py actual te permite hacer lo siguiente: Almacena la información de animation_fps en la imagen. En versiones anteriores de En Android, era necesario configurar animation_fps por tu cuenta.

Para configurar la variable animation_fps, anula el función ScreenRecoveryUI::Init() en tu subclase. Establece el valor y, luego, llama parent Init() para completar la inicialización. El valor predeterminado (20 FPS) corresponde a las imágenes de recuperación predeterminadas; cuando usas estas imágenes, no necesitas proporcionar una función Init() Para obtener detalles sobre las imágenes, consulta Imágenes de recuperación de la IU.

Clase de dispositivo

Cuando tengas una implementación de RecoveryUI, define tu clase de dispositivo (subclase de la la clase de dispositivo integrada). Debería crear una sola instancia de tu clase de IU y mostrar esa de la función GetUI():

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

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

    RecoveryUI* GetUI() { return ui; }

Iniciar recuperación

Se llama al método StartRecovery() al inicio de la recuperación, después de que la IU una vez inicializados y después de analizarlos, pero antes de que se realice una acción tomadas. La implementación predeterminada no realiza ninguna acción, por lo que no es necesario que proporciones esto en tu subclase si no tienes nada que hacer:

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

Proporciona y administra el menú de recuperación

El sistema llama a dos métodos para obtener la lista de líneas de encabezado y la lista de elementos. En este muestra los arrays estáticos definidos en la parte superior del archivo:

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

ClaveMenú

Luego, proporciona una función HandleMenuKey(), que toma una pulsación de teclas y el valor la visibilidad del menú y decide qué acción tomar:

   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;
    }

Este método toma un código de clave (que anteriormente fue procesado y puesto en cola por el CheckKey() del objeto de IU) y el estado actual del menú o registro de texto visibilidad. El valor que se muestra es un número entero. Si el valor es 0 o mayor, se toma como el valor de un elemento del menú, que se invoca inmediatamente (consulta el InvokeMenuItem() a continuación). De lo contrario, puede ser una de las siguientes constantes predefinidas:

  • kHighlightUp. Mover la selección del menú al elemento anterior
  • kHighlightDown. Mover la selección del menú al siguiente elemento
  • kInvokeItem. Invocar el elemento destacado actualmente
  • kNoAction. No hacer nada con esta pulsación de teclas

Como se indica en el argumento visible, se llama a HandleMenuKey() incluso si el menú está no visible. A diferencia de CheckKey(), no se llama durante la recuperación. como limpiar datos o instalar un paquete; solo se llama cuando la recuperación está inactiva y esperando la entrada.

Mecanismos de bola de seguimiento

Si tu dispositivo tiene un mecanismo de entrada similar a una bola de seguimiento (genera eventos de entrada con el tipo EV_REL). y código REL_Y), la recuperación sintetiza las teclas KEY_UP y KEY_DOWN cada vez que El dispositivo de entrada tipo bola de seguimiento informa el movimiento en el eje Y. Solo tienes que asignar KEY_UP y KEY_DOWN en las acciones del menú. Esta asignación no ocurre para CheckKey(), así que no puedes usar movimientos de la bola de seguimiento como disparadores para reiniciar o activando o desactivando la pantalla.

Teclas modificadoras

Para verificar si las teclas se mantienen presionadas como modificadores, llama al método IsKeyPressed() . de tu propio objeto de IU. Por ejemplo, en algunos dispositivos, presionar Alt-W en la recuperación iniciará una limpiar los datos, independientemente de si el menú estaba visible o no. Puedes implementarlo de la siguiente manera:

   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
        }
        ...
    }

Nota: Si el valor visible es falso, no tiene sentido mostrar el valor especial valores que manipulen el menú (mover resaltado, invocar elemento destacado) porque el usuario no puede ver el momento destacado. Sin embargo, puedes mostrar los valores si lo deseas.

InvocaMenuItem

A continuación, proporciona un método InvokeMenuItem() que asigne posiciones de números enteros en el array. de elementos que GetMenuItems() muestra a las acciones. Para el array de elementos en el Ejemplo de tardis:

   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;
        }
    }

Este método puede mostrar cualquier miembro de la enumeración BuildinAction para indicarle al sistema que la tome. (o el miembro NO_ACTION si quieres que el sistema no realice ninguna acción). Este es el lugar Proporcionan una funcionalidad de recuperación adicional más allá de lo que hay en el sistema. Agrega un elemento para ello en tu menú, ejecutarlo aquí cuando se invoque ese elemento de menú, y devolver NO_ACTION para que el sistema no hace nada más.

BuildinAction contiene los siguientes valores:

  • NO_ACTION No realizar ninguna acción.
  • REINICIAR Sal de la recuperación y reinicia el dispositivo normalmente.
  • APPLY_EXT, APPLY_CACHE, APPLY_ADB_SIDELOAD. Instala un paquete de actualización desde varios lugares. Para obtener más información, consulta Transferencia.
  • WIPE_CACHE. Vuelve a formatear solo la partición de caché. No se requiere confirmación, ya que es relativamente inofensivo.
  • WIPE_DATA. Cambia el formato de los datos del usuario y las particiones en caché, también conocidos como datos de fábrica restablecer. Se le solicita al usuario que confirme esta acción antes de continuar.

El último método, WipeData(), es opcional y se llama cada vez que se realiza una limpieza de datos se inicia la operación (ya sea desde la recuperación a través del menú o cuando el usuario elige hacer una restablecer la configuración de fábrica desde el sistema principal). Se llama a este método antes de que se almacenen los datos y cómo se limpian las particiones. Si el dispositivo almacena datos del usuario en cualquier otro lugar que no sean esos dos particiones, debes borrarlo aquí. Debes devolver 0 para indicar el éxito y otro en caso de error, aunque, por el momento, se ignora el valor de retorno. La caché y los datos del usuario y todas las particiones se limpian sin importar si muestra éxito o falla.

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

Fabricar dispositivo

Por último, incluye código estándar al final del archivo recovery_ui.cpp para el make_device() que crea y muestra una instancia de tu clase Device:

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

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

Después de completar el archivo recovery_ui.cpp, lo compilaste y lo vinculas a la recuperación en tu dispositivo. En Android.mk crea una biblioteca estática que contenga solo este archivo 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)

Luego, en la configuración de la placa de este dispositivo, especifica tu biblioteca estática como el valor del TARGET_RECOVERY_UI_LIB.

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

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

Imágenes de IU de recuperación

La interfaz de usuario de recuperación consta de imágenes. Lo ideal es que los usuarios nunca interactúen con la IU: Durante una actualización normal, el teléfono se inicia en la recuperación, llena la barra de progreso de la instalación y se inicia en el sistema nuevo sin intervención del usuario. Si se produce un error en el sistema de actualización, la única acción que puede tomar el usuario es llamar al servicio al cliente.

Una interfaz de solo imagen elimina la necesidad de localización. Sin embargo, a partir de Android 5.0, update puede mostrar una cadena de texto (p.ej., "Installing system update...") junto con la imagen. Para conocer los detalles, consulta Texto de recuperación localizado.

Android 5.0 y versiones posteriores

La IU de recuperación de Android 5.0 y versiones posteriores usan dos imágenes principales: la imagen de error. y la animación de instalación.

imagen que se muestra durante el error OTA

Figura 1: icon_error.png

Imagen que se muestra durante la instalación inalámbrica

Figura 2: icon_installing.png

La animación de instalación se representa como una única imagen PNG con diferentes marcos del animación entrelazada por fila (por eso, la Figura 2 aparece aplastada). Por ejemplo, para un Animación de siete fotogramas de 200 x 200; crea una sola imagen de 200 x 1,400 en la que el primer fotograma sean las filas 0, 7 14, 21, ...; el segundo marco son las filas 1, 8, 15, 22, ...; La imagen combinada incluye un elemento bloque de texto que indica la cantidad de fotogramas de animación y de fotogramas por segundo (FPS). La herramienta bootable/recovery/interlace-frames.py toma un conjunto de marcos de entrada. y las combina en la imagen compuesta necesaria que se usa durante la recuperación.

Las imágenes predeterminadas están disponibles en diferentes densidades y se ubican bootable/recovery/res-$DENSITY/images (p.ej., bootable/recovery/res-hdpi/images). Para usar una imagen estática durante la instalación, solo debes proporcionar la imagen icon_installing.png y establecer el número de marcos en el animación a 0 (el icono de error no está animado; siempre es una imagen estática).

Android 4.x y versiones anteriores

La IU de recuperación de Android 4.x y las versiones anteriores usan la imagen de error (que se muestra arriba) y la Instalación de animaciones y varias imágenes superpuestas:

Imagen que se muestra durante la instalación inalámbrica

Figura 3: icon_installing.png

se muestra la imagen como primera
superponer

Figura 4: icon-installing_overlay01.png

la imagen se muestra como séptimo
superponer

Figura 5: icon_installing_overlay07.png

Durante la instalación, la visualización en pantalla se crea dibujando el archivo icon_installing.png. y dibuja uno de los marcos superpuestos sobre ella en el desplazamiento adecuado. Aquí, en rojo se superpone para resaltar dónde está colocada la superposición sobre la imagen base:

imagen compuesta de
instalar más la primera superposición

Figura 6: Instalando el marco de animación 1 (icon_installing.png + icon_installing_overlay01.png)

imagen compuesta de
instalación más la séptima superposición

Figura 7: Instalando el marco de animación 7 (icon_installing.png + icon_installing_overlay07.png)

Los fotogramas posteriores se muestran dibujando solo la siguiente imagen superpuesta encima de lo que se muestra. allí; la imagen base no se vuelve a dibujar.

La cantidad de fotogramas en la animación, la velocidad deseada y los desplazamientos x e y de la superposición en relación con la base son establecidos por variables de miembro de la clase ScreenRecoveryUI. Al usar imágenes personalizadas en lugar de las predeterminadas, anula el método Init() en tu subclase para cambiar estos valores para tus imágenes personalizadas (para obtener más información, consulta ScreenRecoveryUI). El guion bootable/recovery/make-overlay.py puede ayudar a convertir un conjunto de marcos de imagen. a la columna “imagen base + imágenes superpuestas” la recuperación, lo que incluye el procesamiento de la las compensaciones necesarias.

Las imágenes predeterminadas se encuentran en bootable/recovery/res/images. Cómo usar una imagen estática durante la instalación, solo debes proporcionar la imagen icon_installing.png y establecer la cantidad de fotogramas de la animación a 0 (el ícono de error no está animado; siempre es una imagen estática).

Texto de recuperación localizado

Android 5.x muestra una cadena de texto (p.ej., "Instalando la actualización del sistema...") junto con el imagen. Cuando el sistema principal se inicia en la recuperación, pasa la configuración regional actual del usuario como de línea de comandos a recuperación. Para cada mensaje que se muestra, la recuperación incluye un segundo con cadenas de texto renderizadas previamente para ese mensaje en cada configuración regional.

Imagen de ejemplo de cadenas de texto de recuperación:

imagen del texto de recuperación

Figura 8: Texto localizado para los mensajes de recuperación

El texto de recuperación puede mostrar los siguientes mensajes:

  • Instalando actualización del sistema...
  • Error
  • Borrando... (al limpiar los datos o restablecer la configuración de fábrica)
  • Ningún comando (cuando un usuario inicia la recuperación de forma manual)

La app para Android en bootable/recovery/tools/recovery_l10n/ renderiza localizaciones de un mensaje y crea la imagen compuesta. Para obtener detalles sobre el uso de esta app, consulta la comentarios en bootable/recovery/tools/recovery_l10n/src/com/android/recovery_l10n/Main.java

Cuando un usuario inicia la recuperación de forma manual, es posible que la configuración regional no esté disponible y que no se muestre texto que se muestra. No hagas que los mensajes de texto sean críticos para el proceso de recuperación.

Nota: La interfaz oculta que muestra mensajes de registro y permite al usuario Seleccionar acciones del menú solo está disponible en inglés.

Barras de progreso

Pueden aparecer barras de progreso debajo de la imagen principal (o animación). La barra de progreso se crea combinar dos imágenes de entrada, que deben ser del mismo tamaño:

barra de progreso vacía

Figura 9: progress_empty.png

barra de progreso completa

Figura 10: progress_fill.png

El extremo izquierdo de la imagen de fill se muestra junto al extremo derecho de la vacía para crear la barra de progreso. La posición del límite entre ambos y las imágenes se cambian para indicar el progreso. Por ejemplo, con los pares de imágenes de entrada anteriores, visualización:

barra de progreso al 1%

Figura 11: Barra de progreso en 1%>

la barra de progreso al 10%

Figura 12: Barra de progreso al 10%

la barra de progreso al 50%

Figura 13: Barra de progreso al 50%

Puedes proporcionar versiones de estas imágenes específicas para un dispositivo colocándolas en (en este ejemplo) device/yoyodyne/tardis/recovery/res/images de Google Cloud. Los nombres de archivo deben coincidir con los que se mencionaron anteriormente. Cuando se encuentra un archivo en ese directorio, el de compilación lo usa en lugar de la imagen predeterminada correspondiente. Solo se admiten los archivos PNG en RGB o Se admite el formato RGBA con profundidad de color de 8 bits.

Nota: En Android 5.x, si la configuración regional es conocida por la recuperación y es una idioma de derecha a izquierda (RTL) (árabe, hebreo, etc.), la barra de progreso se rellena de derecha a izquierda.

Dispositivos sin pantallas

No todos los dispositivos Android tienen pantalla. Si tu dispositivo no tiene interfaz gráfica o tiene de solo audio, es posible que debas personalizar la IU de recuperación de forma más amplia. En cambio, de crear una subclase de ScreenRecoveryUI, subclasifica su clase superior RecoveryUI directamente.

RecoveryUI tiene métodos para controlar operaciones de IU de nivel inferior, como "activar o desactivar la pantalla". "actualizar la barra de progreso" "mostrar el menú", "cambiar la selección del menú", etc. Se puede anular para ofrecer una interfaz adecuada para tu dispositivo. Tal vez el dispositivo tenga luces LED puedes usar diferentes colores o patrones de intermitencia para indicar el estado, o tal vez puedes audio. (Tal vez no desees admitir un menú o el modo de "visualización de texto" en absoluto; puedes impide el acceso con CheckKey() y Implementaciones de HandleMenuKey() que nunca activan la visualización ni seleccionan un menú elemento. En este caso, muchos de los métodos de RecoveryUI que debes proporcionar pueden estar vacíos. stubs).

Consulta bootable/recovery/ui.h para ver la declaración de RecoveryUI y ver qué métodos. que debes admitir. RecoveryUI es abstracto: algunos métodos son virtuales puros y los debe proporcionar subclases, pero contiene el código para procesar las entradas clave. Puedes anular ese si tu dispositivo no tiene llaves o quieres procesarlas de manera diferente.

Google Updater

Puedes usar un código específico del dispositivo en la instalación del paquete de actualización proporcionando tu propias funciones de extensión a las que se puede llamar desde la secuencia de comandos del actualizador. Aquí hay un ejemplo para el dispositivo de Tardis:

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

#include "edify/expr.h"

Cada función de extensión tiene la misma firma. Los argumentos son el nombre con el que una función, una cookie State*, la cantidad de argumentos entrantes y un array de punteros Expr* que representan los argumentos El valor que se devuelve es un Value* recién asignado.

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);
    }

Tus argumentos no se evaluaron en el momento en que se llama a tu función; el bloque de la lógica determina cuáles de ellos se evalúan y cuántas veces. Por lo tanto, puedes usar extensiones para implementar tus propias estructuras de control. Call Evaluate() para evaluar Un argumento Expr* que muestra un Value* Si Evaluate() devuelve NULL, debes liberar los recursos que tengas y devolver inmediatamente NULL (este propaga, anula la pila de edify). De lo contrario, usted asumirá la propiedad del valor devuelto y son responsables de llamar en algún momento FreeValue().

Supongamos que la función necesita dos argumentos: una clave con valor de cadena y otra con valor de BLOB imagen. Puedes leer argumentos como el siguiente:

   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;
    }

Comprobar si hay NULL y liberar argumentos previamente evaluados puede ser tedioso para varias argumentos. La función ReadValueArgs() puede facilitar este proceso. En lugar del código, arriba, podrías haber escrito esto:

   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() no realiza comprobación de tipo, por lo que debes hacerlo aquí; es más conveniente hacerlo con una instrucción if, a costa de producir una reducción un mensaje de error específico cuando falla. Pero ReadValueArgs() sí se encarga de evaluar cada argumento y liberar todos los argumentos previamente evaluados (además de establecer una lista útil mensaje de error) si falla alguna de las evaluaciones. Puedes usar un La función conveniente ReadValueVarArgs() para evaluar una cantidad variable de argumentos (devuelve un array de Value*).

Después de evaluar los argumentos, haz el trabajo de la función:

   // 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 ...

El valor que se muestra debe ser un objeto Value*. la propiedad de este objeto pasará a el llamador. El llamador se hace propietario de todos los datos a los que hace referencia este Value*: En particular, el miembro de datos.

En este caso, debes mostrar un valor verdadero o falso para indicar que la operación fue exitosa. ¿Recuerdas convención de que la cadena vacía es false y todas las demás cadenas son true. Tú debe malloc un objeto Value con una copia malloc de la cadena constante para devolver, ya que la el llamador ejecutará free() ambas. No olvides llamar a FreeValue() al objetos que obtuviste al evaluar tus argumentos.

   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 función conveniente StringValue() une una cadena en un nuevo objeto Value. Se usa para escribir el código anterior de forma más concisa:

   FreeValue(key);
    FreeValue(image);

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

Para conectar funciones al intérprete de edify, proporciona la función Register_foo en el que foo es el nombre de la biblioteca estática que contiene este código. Llama a RegisterFunction() para registrar cada función de extensión. De convención, nombrar funciones específicas del dispositivo device.whatever para evitar conflictos con futuras funciones integradas.

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

Ahora puedes configurar el archivo makefile para compilar una biblioteca estática con tu código. (es el mismo El archivo makefile se usa para personalizar la IU de recuperación en la sección anterior. tu dispositivo puede tener ambos estáticas, definidas aquí).

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

El nombre de la biblioteca estática debe coincidir con el nombre de la función Register_libname que contiene.

LOCAL_MODULE := librecovery_updater_tardis
include $(BUILD_STATIC_LIBRARY)

Por último, configura la compilación de recuperación para extraer tu biblioteca. Agrega tu biblioteca a TARGET_RECOVERY_UPDATER_LIBS (que puede contener varias bibliotecas; todas se registran). Si tu código depende de otras bibliotecas estáticas que no son extensiones de edify (es decir, no tiene una función Register_libname), puedes enumerarlas en TARGET_RECOVERY_UPDATER_EXTRA_LIBS para vincularlos al actualizador sin llamar a su (inexistente). Por ejemplo, si el código específico de tu dispositivo quisiera usar zlib para descomprimir datos, deberías incluir libz aquí.

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 +=

Las secuencias de comandos del actualizador en tu paquete inalámbrico ahora pueden llamar a tu función como cualquier otra. Para reprogramar tu dispositivo Tardis, la secuencia de comandos de actualización puede contener lo siguiente: tardis.reprogram("the-key", package_extract_file("tardis-image.dat")) Esto usa la versión de un solo argumento de la función integrada package_extract_file() que devuelve el contenido de un archivo extraído del paquete de actualización como un BLOB para producir el segundo argumento a la nueva función de extensión.

Generación de paquetes inalámbricos

El último componente es hacer que las herramientas de generación de paquetes OTA conozcan datos específicos del dispositivo y emiten secuencias de comandos del actualizador que incluyen llamadas a las funciones de tu extensión.

Primero, haz que el sistema de compilación conozca un BLOB de datos específico del dispositivo. Suponiendo que tus datos está en device/yoyodyne/tardis/tardis.dat, declara lo siguiente en tu AndroidBoard.mk del dispositivo:

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

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

También puedes colocarlo en un archivo Android.mk, pero luego debe estar protegido por un dispositivo. ya que todos los archivos Android.mk del árbol se cargan, independientemente del dispositivo que se esté usando. construyen. (Si el árbol incluye varios dispositivos, solo querrás que el archivo tardis.dat se agregue cuando compilando el dispositivo 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

Se llaman archivos de radio por razones históricas. es posible que no tengan nada que ver radio del dispositivo (si está presente). Son simples BLOB opacos de datos que el sistema de compilación copia en en el archivo target-files .zip que usan las herramientas de generación OTA. Cuando haces una compilación, el archivo tardis.dat almacenado en target-files.zip como RADIO/tardis.dat. Puedes llamar add-radio-file varias veces para agregar todos los archivos que desees.

Módulo de Python

Para extender las herramientas de lanzamiento, escribe un módulo de Python (debe llamarse releasetools.py) con las herramientas a los que podemos llamar si están presentes. Ejemplo:

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"));""")

Una función independiente se encarga de generar un paquete inalámbrico incremental. Para este ejemplo, supongamos que necesitas reprogramar los tardis solo cuando el archivo tardis.dat haya cambiado entre dos compilaciones.

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"));""")

Funciones del módulo

Puedes proporcionar las siguientes funciones en el módulo (implementa solo las que necesitas).

FullOTA_Assertions()
Se llama cerca del comienzo de la generación de una OTA completa. Este es un buen lugar para emitir aserciones sobre el estado actual del dispositivo. No emita comandos de secuencia de comandos que realicen cambios en la dispositivo.
FullOTA_InstallBegin()
Se llama después de que se pasan todas las aserciones sobre el estado del dispositivo, pero antes de cualquier cambio. de datos. Puedes emitir comandos para actualizaciones específicas del dispositivo que deben ejecutarse se modificó cualquier otra cosa en el dispositivo.
FullOTA_InstallEnd()
Se llama al final de la generación de la secuencia de comandos, después de los comandos de la secuencia de comandos para actualizar las configuraciones o particiones del sistema. También puedes emitir comandos adicionales para actualizaciones específicas del dispositivo.
IncrementalOTA_Assertions()
Similar a FullOTA_Assertions(), pero se llama cuando se genera un de actualización del paquete.
IncrementalOTA_VerifyBegin()
Se llama después de que se pasan todas las aserciones sobre el estado del dispositivo, pero antes de que se implementen cambios. en la nube. Puedes emitir comandos para actualizaciones específicas del dispositivo que deben ejecutarse antes que cualquier elemento más en el dispositivo se ha cambiado.
IncrementalOTA_VerifyEnd()
Se llama al final de la fase de verificación, cuando la secuencia de comandos termina de confirmar el que los archivos que tocará tengan el contenido inicial esperado. En este momento, no hay nada en el se cambió el dispositivo. También puedes emitir un código para cumplir con requisitos adicionales verificaciones de identidad.
IncrementalOTA_InstallBegin()
Se llama después de que se verifica que los archivos a los que se deben aplicar los parches tienen los el estado before, pero antes de que se realicen cambios. Puedes emitir comandos para actualizaciones específicas del dispositivo que deben ejecutarse antes que cualquier otro cambio en el dispositivo.
IncrementalOTA_InstallEnd()
De manera similar a su equivalente de paquete inalámbrico completo, se llama al final de la secuencia de comandos. una vez que se hayan ejecutado los comandos de secuencia de comandos para actualizar las particiones emitidos. También puedes emitir comandos adicionales para obtener actualizaciones específicas del dispositivo.

Nota: Si el dispositivo se queda sin energía, es posible que la instalación inalámbrica se reinicie desde el empezando. Prepárate para manejar los dispositivos en los que ya se ejecutaron estos comandos total o parcialmente.

Cómo pasar funciones a objetos de información

Pasa funciones a un solo objeto de información que contenga varios elementos útiles:

  • info.input_zip. (Solo OTA completas) El objeto zipfile.ZipFile del archivo .zip de entrada de destino.
  • info.source_zip. (Solo para actualizaciones inalámbricas incrementales) El objeto zipfile.ZipFile para el archivo source target-files .zip (la compilación que ya está en el dispositivo cuando el paquete incremental se está instalando).
  • info.target_zip. (Solo para actualizaciones inalámbricas incrementales) El objeto zipfile.ZipFile para El archivo target-files .zip (la compilación que el paquete incremental coloca en el dispositivo)
  • info.output_zip; Se está creando el paquete; se abrió un objeto zipfile.ZipFile para escribir. Usa Commons.ZipWriteStr(info.output_zip, filename, data) para agregar un al paquete.
  • info.script: Objeto de secuencia de comandos al que puedes agregar comandos. Llamada info.script.AppendExtra(script_text) para generar texto en la secuencia de comandos. Asegúrate de que el texto de salida termine con un punto y coma para que no se encuentre con comandos emitidos después.

Para obtener detalles sobre el objeto de información, consulta el Documentación de Python Software Foundation para archivos ZIP.

Cómo especificar la ubicación del módulo

Especifica la ubicación de la secuencia de comandos releasetools.py de tu dispositivo en el archivo BoardConfig.mk:

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

TARGET_RELEASETOOLS_EXTENSIONS := device/yoyodyne/tardis

Si TARGET_RELEASETOOLS_EXTENSIONS no está configurado, el valor predeterminado será el Directorio $(TARGET_DEVICE_DIR)/../common (device/yoyodyne/common en este ejemplo). Es mejor definir explícitamente la ubicación de la secuencia de comandos releasetools.py. Cuando compilas el dispositivo de tardis, se incluye la secuencia de comandos releasetools.py en los archivos target-files Archivo ZIP (META/releasetools.py ).

Cuando ejecutes las herramientas de lanzamiento (ya sea img_from_target_files o ota_from_target_files), la secuencia de comandos releasetools.py en el archivo target-files .zip presente, en lugar de la del árbol de fuentes de Android. También puedes especificar especificar la ruta de acceso a las extensiones específicas del dispositivo con el -s (o --device_specific), que toma la prioridad superior. Esto te permite corregir errores y realizar cambios en las extensiones de las herramientas de lanzamiento, y aplicar esos cambios target-files

Ahora, cuando ejecutes ota_from_target_files, este detectará automáticamente la módulo específico para el dispositivo a partir del archivo ZIP target_files y lo usa cuando se genera una actualización inalámbrica paquetes:

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

Como alternativa, puedes especificar extensiones específicas para dispositivos cuando ejecutes 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

Nota: Para obtener una lista completa de opciones, consulta la ota_from_target_files comentarios en build/make/tools/releasetools/ota_from_target_files

Mecanismo de transferencia

La recuperación tiene un mecanismo de transferencia para instalar de forma manual un paquete de actualización sin de forma inalámbrica mediante el sistema principal. La transferencia es útil para depurar o hacer cambios en dispositivos donde no se puede iniciar el sistema principal.

Históricamente, la transferencia se realizaba mediante la carga de paquetes fuera de la tarjeta SD del dispositivo. en En el caso de los dispositivos que no son de arranque, puedes colocar el paquete en la tarjeta SD usando otro computadora y luego la tarjeta SD insertada en el dispositivo. Para adaptarse a dispositivos Android sin En un almacenamiento externo extraíble, la recuperación admite dos mecanismos adicionales de transferencia: cargar paquetes desde la partición de caché y cargarlos mediante USB mediante adb.

Para invocar cada mecanismo de transferencia, el método Device::InvokeMenuItem() de tu dispositivo puede mostrar los siguientes valores de BuildinAction:

  • APPLY_EXT. Transferir un paquete de actualización desde un almacenamiento externo ( /sdcard directorio). El archivo retrieve.fstab debe definir el punto de activación /sdcard . Este es No se puede usar en dispositivos que emulan una tarjeta SD con un symlink a /data (o con algunas mecanismo similar). Por lo general, /data no está disponible para la recuperación pueden estar encriptados. La IU de recuperación muestra un menú de archivos ZIP en /sdcard y permite que el usuario seleccione una.
  • APPLY_CACHE. Es similar a cargar un paquete desde /sdcard, con la excepción de que Se usa el directorio /cache (que está siempre disponible para la recuperación) en su lugar. Desde el sistema normal, solo los usuarios con privilegios pueden escribir en /cache , y, si el dispositivo no se puede iniciar, no se podrá escribir en el directorio /cache. en absoluto (lo que hace que este mecanismo tenga utilidad limitada).
  • APPLY_ADB_SIDELOAD. Permite al usuario enviar un paquete al dispositivo a través de un cable USB y la herramienta de desarrollo adb. Cuando se invoca este mecanismo, la recuperación inicia su propio servicio del daemon adbd para permitir que adb en un equipo host conectado se comunique con él. Este mini solo admite un único comando: adb sideload filename. El archivo nombrado se envía de la máquina anfitrión al dispositivo, que luego verifica y lo instala como si hubiera estado en el almacenamiento local.

Algunas advertencias:

  • Solo se admite el transporte USB.
  • Si tu recuperación ejecuta adbd normalmente (generalmente, verdadero para las compilaciones userdebug y eng), se cerrará mientras el dispositivo esté en modo de transferencia de adb y se reiniciará cuando adb sideload terminó de recibir un paquete. En el modo de transferencia de adb, no hay comandos de adb que sideload trabajo ( logcat, reboot, push, pull , shell, etc. fallan).
  • No puedes salir del modo de transferencia de adb en el dispositivo. Para anularla, puedes enviar /dev/null (o cualquier otro que no sea un paquete válido) como paquete. el dispositivo no podrá verificarlo y detendrá el procedimiento de instalación. La IU de recuperación se seguirá llamando al método CheckKey() de la implementación para pulsaciones de teclas de modo que puedas proporcionar una secuencia de claves que reinicie el dispositivo y funcione en el modo de transferencia de adb.