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(); }
Cómo compilar y vincular a la recuperación del dispositivo
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.
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:
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:
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:
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:
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:
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étodoCheckKey()
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.