O sistema de recuperação inclui vários ganchos para inserir código específico do dispositivo, de modo que o OTA as atualizações também podem atualizar partes do dispositivo que não sejam o sistema Android (por exemplo, a banda de base ou processador de rádio).
As seções e os exemplos a seguir personalizam o dispositivo tardis produzido pelo fornecedor yoyodyne.
Mapa de partições
A partir do Android 2.3, a plataforma oferece suporte a dispositivos flash eMMc e ao sistema de arquivos ext4 que é executado nesses dispositivos. Ele também é compatível com dispositivos flash de dispositivos de tecnologia de memória (MTD, na sigla em inglês) e o sistema de versões mais antigas.
O arquivo do mapa de partições é especificado por TARGET_RECOVERY_FSTAB; que o arquivo seja usado e das ferramentas de criação de pacotes. Você pode especificar o nome do arquivo de mapa em TARGET_RECOVERY_FSTAB em BoardConfig.mk.
Um exemplo de arquivo de mapa de partições pode ter a seguinte aparência:
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
Com exceção de /sdcard
, que é opcional, todos os pontos de montagem neste
exemplo precisa ser definido (os dispositivos também podem adicionar partições extras). Há cinco suportes
tipos de sistema de arquivos:
- yaffs2
-
Um sistema de arquivos yaffs2 sobre um dispositivo flash MTD. "dispositivo" precisa ser o nome da partição MTD
e precisa aparecer em
/proc/mtd
. - mtd
-
Uma partição MTD bruta, usada para partições inicializáveis, como inicialização e recuperação. O MTD não é
montado, mas o ponto de montagem é usado como chave para localizar a partição. "dispositivo"
precisa ser o nome da partição MTD em
/proc/mtd
. - ext4
- Um sistema de arquivos ext4 sobre um dispositivo flash eMMc. "dispositivo" precisa ser o caminho do dispositivo de transferência por blocos.
- Emmc
- Um dispositivo de bloco eMMc bruto, usado para partições inicializáveis, como inicialização e recuperação. Semelhante a do tipo mtd, eMMc nunca é realmente montado, mas a string do ponto de montagem é usada para localizar o dispositivo na mesa.
- vfat
-
Um sistema de arquivos FAT sobre um dispositivo de transferência por blocos, normalmente para armazenamento externo, como um cartão SD. A
device é o dispositivo de bloco, device2 é um segundo dispositivo em bloco que o sistema tenta montar caso
a montagem do dispositivo principal falha (para compatibilidade com cartões SD, que podem ou não ser
formatados com uma tabela de partições).
Todas as partições precisam ser montadas no diretório raiz (ou seja, o valor do ponto de montagem precisa comece com uma barra e não tenha outras barras). Essa restrição se aplica apenas à montagem sistemas de arquivos em recuperação, o sistema principal é livre para montá-los em qualquer lugar. Os diretórios
/boot
,/recovery
e/misc
precisam ser tipos brutos (mtd ou emmc), enquanto os diretórios/system
,/data
,/cache
e/sdcard
(se disponível) devem ser tipos de sistema de arquivos (yaffs2, ext4 ou vfat).
A partir do Android 3.0, o arquivo recovery.fstab recebe mais um campo opcional: opções. Atualmente, a única opção definida é length , que permite explicitamente especificar o comprimento da partição. Esse tamanho é usado ao reformatar a partição (por exemplo, para a partição de dados do usuário durante uma operação de limpeza de dados/redefinição de fábrica, ou para o partição do sistema durante a instalação de um pacote OTA completo). Se o valor de comprimento for negativo, o tamanho a ser formatado é usado adicionando o valor do comprimento ao tamanho real da partição. Para instância, definindo “length=-16384” significa que os últimos 16 K dessa partição não serão substituída quando a partição for reformatada. Ele dá suporte a recursos como criptografia na partição userdata (onde os metadados de criptografia são armazenados no final da partição que não deve ser substituída).
Observação: os campos device2 e options são opcionais, criando ambiguidade na análise. Se a entrada no quarto campo da linha começar com "/" ela será considerada uma entrada device2 ; se a entrada não começar com "/" ele será considerado um campo options.
Animação de inicialização
Os fabricantes de dispositivos podem personalizar a animação exibida quando um dispositivo Android está inicializando. Para isso, construa um arquivo ZIP organizado e localizado de acordo com as especificações em formato bootAnimation.
Para Dispositivos Android Things, faça upload do arquivo compactado no console do Android Things para incluir as imagens no o produto selecionado.
Observação:essas imagens precisam atender às diretrizes da promoção de marca do Android. Para as diretrizes de marca, consulte a seção do Android Marketing do parceiro Hub.
interface de recuperação
Para oferecer suporte a dispositivos com diferentes hardwares disponíveis (botões físicos, LEDs, telas etc.), você pode personalizar a interface de recuperação para exibir o status e acessar o sistema recursos ocultos para cada dispositivo.
Seu objetivo é criar uma pequena biblioteca estática com alguns objetos C++ para fornecer a
funcionalidades específicas do dispositivo. O arquivo
bootable/recovery/default_device.cpp
é usado por padrão e é um bom
ponto de partida a ser copiado ao gravar uma versão deste arquivo para seu dispositivo.
Observação:a mensagem No Command talvez apareça aqui. Para alternar texto, mantenha o botão liga/desliga pressionado enquanto pressiona o botão de aumentar o volume. Se seus dispositivos não tiverem os dois botões. Mantenha qualquer botão pressionado para alternar o texto.
device/yoyodyne/tardis/recovery/recovery_ui.cpp
#include <linux/input.h> #include "common.h" #include "device.h" #include "screen_ui.h"
Funções de cabeçalho e item
A classe Device requer funções para retornar cabeçalhos e itens que aparecem no no menu de recuperação. Os cabeçalhos descrevem como operar o menu (ou seja, controles para alterar/selecionar o item 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 };
Observação:as linhas longas são truncadas (não quebradas). Portanto, mantenha a largura das da tela do dispositivo em mente.
Personalizar chave de verificação
Em seguida, defina a implementação da RecoveryUI do dispositivo. Neste exemplo, pressupomos
O dispositivo tardis tem uma tela, que pode ser herdada do sistema
ScreenRecoveryUIimplementation (consulte as instruções para
dispositivos sem tela. A única função para
personalizar da ScreenRecoveryUI é CheckKey()
, que faz o
processamento assíncrono de chaves:
class TardisUI : public ScreenRecoveryUI { public: virtual KeyAction CheckKey(int key) { if (key == KEY_HOME) { return TOGGLE; } return ENQUEUE; } };
Constantes KEY
As constantes KEY_* são definidas em linux/input.h
. CheckKey()
é
chamados, não importa o que esteja acontecendo no restante da recuperação: quando o menu está desativado, quando
estiver ativado, durante a instalação do pacote, durante a limpeza de dados do usuário etc. Ele pode retornar um dos quatro
constantes:
- ATIVAR. Ativar ou desativar a exibição do menu e/ou o registro de texto
- REINICIAR. Reinicie o dispositivo imediatamente
- IGNORAR. Ignorar esta pressionamento de tecla
- NA FILA. Enfileirar esta tecla pressionada para consumo síncrono (ou seja, pelo sistema de recuperação sistema de menu se a tela estiver ativada)
CheckKey()
é chamado sempre que um evento de tecla para baixo é seguido por um evento de pressionamento de tecla para
a mesma chave. (A sequência de eventos A-down B-down B-up A-up resulta apenas em
CheckKey(B)
sendo chamado. CheckKey()
pode ligar
IsKeyPressed()
, para descobrir se outras teclas estão sendo pressionadas. (No exemplo acima,
sequência de eventos de tecla, se CheckKey(B)
chamou IsKeyPressed(A)
,
retornaria "true".
CheckKey()
pode manter o estado na classe. isso pode ser útil para detectar
sequências de chaves. Este exemplo mostra uma configuração um pouco mais complexa: a tela é alternada pela
Manter o botão liga/desliga pressionado e pressionar o botão de aumentar volume. O dispositivo pode ser reiniciado imediatamente
pressionando o botão liga/desliga cinco vezes seguidas (sem outras teclas intermediárias):
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; } };
Interface de recuperação de tela
Ao usar suas próprias imagens (ícone de erro, animação de instalação, barras de progresso) com
ScreenRecoveryUI, é possível definir a variável animation_fps
para controlar a velocidade;
quadros por segundo (QPS) das animações.
Observação: o script interlace-frames.py
atual permite que você:
armazene as informações de animation_fps
na própria imagem. Em versões anteriores do
Android, era necessário definir o animation_fps
por conta própria.
Para definir a variável animation_fps
, modifique o
ScreenRecoveryUI::Init()
na subclasse. Defina o valor e chame o método
Função parent Init()
para concluir a inicialização. O valor padrão (20 QPS)
corresponde às imagens de recuperação padrão. ao usar essas imagens, você não precisa fornecer
uma função Init()
. Para detalhes sobre imagens, consulte
Imagens da interface de recuperação.
Classe do dispositivo
Depois de implementar a RecoveryUI, defina a classe do dispositivo, que é uma subclasse da
classe do dispositivo integrada). Ele deve criar uma única instância da sua classe de interface e retornar essa
da função GetUI()
:
class TardisDevice : public Device { private: TardisUI* ui; public: TardisDevice() : ui(new TardisUI) { } RecoveryUI* GetUI() { return ui; }
IniciarRecuperação
O método StartRecovery()
é chamado no início da recuperação, depois que a interface é
inicializado e após os argumentos terem sido analisados, mas antes que qualquer ação tenha sido
tomadas. A implementação padrão não faz nada. Por isso, não é necessário fornecer isso no
subclasse se você não tem nada a fazer:
void StartRecovery() { // ... do something tardis-specific here, if needed .... }
Menu de recuperação de fornecimento e gerenciamento
O sistema chama dois métodos para receber a lista de linhas de cabeçalho e a lista de itens. Neste implementação, ela retorna as matrizes estáticas definidas na parte superior do arquivo:
const char* const* GetMenuHeaders() { return HEADERS; } const char* const* GetMenuItems() { return ITEMS; }
HandleMenuKey
Em seguida, forneça uma função HandleMenuKey()
, que usa um pressionamento de tecla e o
a visibilidade do menu e decide a ação a ser tomada:
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; }
O método usa um código-chave (que foi previamente processado e colocado na fila pelo
método CheckKey()
do objeto da interface) e o estado atual do menu/registro de texto
visibilidade. O valor de retorno é um número inteiro. Se o valor for 0 ou maior, ele será usado como
posição de um item de menu, que é invocada imediatamente (consulte a
InvokeMenuItem()
abaixo). Caso contrário, pode ser um dos seguintes:
constantes predefinidas:
- kHighlightUp: Mover o destaque do menu para o item anterior
- kHighlightDown (em inglês). Mover o destaque do menu para o próximo item
- kChamadasItem. Invocar o item destacado no momento
- kNoAction. Não fazer nada com esta tecla pressionada
Como implícito pelo argumento visible, HandleMenuKey()
é chamado mesmo se o menu for
visível. Ao contrário de CheckKey()
, ele não é chamado durante a recuperação.
algo como a exclusão permanente dos dados ou a instalação de um pacote; esse método só é chamado quando a recuperação está ociosa.
e aguardando as informações.
Mecanismos de trackball
Se o dispositivo tiver um mecanismo de entrada semelhante a trackball (gera eventos de entrada com o tipo EV_REL)
e o código REL_Y), a recuperação sintetiza as teclas KEY_UP e KEY_DOWN sempre que a
o dispositivo de entrada semelhante ao trackball informa o movimento no eixo Y. Tudo que você precisa fazer é mapear KEY_UP e
KEY_DOWN em ações do menu. Esse mapeamento não acontece para
CheckKey()
, portanto, não é possível usar os movimentos do trackball como acionadores para reinicialização ou
alternando a tela.
Teclas modificadoras
Para conferir se há teclas pressionadas como modificadores, chame o método IsKeyPressed()
.
do seu próprio objeto de IU. Por exemplo, em alguns dispositivos, pressionar Alt-W na recuperação inicia uma
a exclusão permanente de dados, independentemente de o menu estar visível ou não. Você poderia implementar da seguinte forma:
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 } ... }
Observação:se visible for falso, não faz sentido retornar o valor especial valores que manipulam o menu (mover o destaque, invocar o item destacado), já que o usuário não pode o destaque. No entanto, é possível retornar os valores, se você quiser.
InvocarMenuItem
Em seguida, forneça um método InvokeMenuItem()
que mapeie posições de números inteiros na matriz
de itens retornados por GetMenuItems()
para ações. Para a matriz de itens na
exemplo de tardis, use:
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; } }
Esse método pode retornar qualquer membro do tipo enumerado EmbedinAction para instruir o sistema a realizar essa ação. ação (ou o membro NO_ACTION se desejar que o sistema não faça nada). Este é o lugar para fornecem funcionalidade de recuperação adicional, além do que está no sistema: adicionar um item ao seu menu, executá-lo aqui quando o item de menu for invocado e retornar NO_ACTION para que o sistema não faz mais nada.
builtinAction contém os seguintes valores:
- NO_ACTION. Não fazer nada.
- REINICIAR. Saia da recuperação e reinicie o dispositivo normalmente.
- APPLY_EXT, APPLY_CACHE, APPLY_ADB_SIDELOAD. Instalar pacotes de atualização de vários lugares. Para mais detalhes, consulte Sideload.
- LIMPAR_CACHE. Reformate apenas a partição de cache. Nenhuma confirmação é necessária, porque isso é relativamente inofensivos.
- WIPE_DATA. Reformatar as partições de cache e userdata, também conhecidas como dados de fábrica redefinido. O usuário precisa confirmar a ação antes de continuar.
O último método, WipeData()
, é opcional e é chamado sempre que uma exclusão permanente de dados acontece.
for iniciada (na recuperação pelo menu ou quando o usuário optar por realizar uma
redefinir para a configuração original pelo sistema principal). Esse método é chamado antes dos dados do usuário e do cache
as partições são apagadas. Se o dispositivo armazenar dados do usuário em qualquer lugar além desses dois
partições diferentes, apague-as aqui. Você deve retornar 0 para indicar sucesso e outro
valor de falha, embora atualmente o valor de retorno seja ignorado. Os dados e o cache do usuário
as partições são excluídas, mesmo que
você retorne sucesso ou falha.
int WipeData() { // ... do something tardis-specific here, if needed .... return 0; }
Fabricante do dispositivo
Por fim, inclua um código boilerplate no final do arquivo recovery_ui.cpp para
Função make_device()
que cria e retorna uma instância da classe Device:
class TardisDevice : public Device { // ... all the above methods ... }; Device* make_device() { return new TardisDevice(); }
Criar e vincular à recuperação de dispositivos
Depois de concluir o arquivo recovery_ui.cpp, crie-o e vincule-o à recuperação no seu dispositivo. Em Android.mk, crie uma biblioteca estática que contenha apenas esse arquivo 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)
Em seguida, na configuração da placa deste dispositivo, especifique sua biblioteca estática como o valor de TARGET_RECOVERY_UI_LIB.
device/yoyodyne/tardis/BoardConfig.mk [...] # device-specific extensions to the recovery UI TARGET_RECOVERY_UI_LIB := librecovery_ui_tardis
Imagens da interface de recuperação
A interface do usuário de recuperação consiste em imagens. O ideal é que os usuários nunca interajam com a interface: Durante uma atualização normal, o smartphone inicializa na recuperação, preenche a barra de progresso da instalação, e inicializa de volta no novo sistema sem interação do usuário. Caso haja um sistema problema de atualização, a única ação do usuário que pode ser realizada é ligar para o atendimento ao cliente.
Uma interface apenas de imagem elimina a necessidade de localização. No entanto, a partir do Android 5.0, atualização pode exibir uma sequência de texto (por exemplo, "Instalando atualização do sistema...") junto com a imagem. Para mais detalhes, consulte Texto de recuperação localizado.
Android 5.0 e mais recente
A interface de recuperação do Android 5.0 e versões posteriores usa duas imagens principais: a imagem de erro e a animação de instalação.
A animação de instalação é representada como uma única imagem PNG com frames diferentes do
animação entrelaçada por linha (por isso, a Figura 2 aparece apertada). Por exemplo, para um
Animação com sete frames de 200 x 200, crie uma única imagem de 200 x 1400 com o primeiro frame das linhas 0, 7
14, 21, ...; o segundo frame é: linhas 1, 8, 15, 22, ...; A imagem combinada inclui uma
bloco de texto que indica o número de frames de animação e o número de frames por segundo
(QPS). A ferramenta bootable/recovery/interlace-frames.py
usa um conjunto de frames de entrada
e as combina na imagem composta necessária usada pela recuperação.
As imagens padrão estão disponíveis em diferentes densidades e estão localizadas
bootable/recovery/res-$DENSITY/images
(por exemplo,
bootable/recovery/res-hdpi/images
). Para usar uma imagem estática durante a instalação,
você só precisa fornecer a imagem icon_installing.png e definir o número de frames no
animação para 0 (o ícone de erro não é animado, é sempre uma imagem estática).
Android 4.x e anterior
A interface de recuperação do Android 4.x e versões anteriores usam a imagem de erro (mostrada acima) e as installing e várias imagens de sobreposição:
Durante a instalação, a tela é construída desenhando o icon_installing.png e desenhando um dos frames de sobreposição em cima dela no deslocamento adequado. Aqui, um vermelho é sobreposta para destacar onde a sobreposição é colocada em cima da imagem de base:
Os frames seguintes são exibidos desenhando apenas a próxima imagem de sobreposição sobre a já está lá; a imagem base não será redesenhada.
O número de frames na animação, a velocidade desejada e os deslocamentos x e y da sobreposição
em relação à base são definidos por variáveis de membro da classe ScreenRecoveryUI. Ao usar
imagens personalizadas em vez de imagens padrão, modifique o método Init()
na
para alterar esses valores nas imagens personalizadas (para obter detalhes, consulte
ScreenRecoveryUI). O script
O bootable/recovery/make-overlay.py
pode ajudar na conversão de um conjunto de frames de imagem
"imagem de base + imagens de sobreposição" necessário para a recuperação, incluindo o cálculo da
os deslocamentos necessários.
As imagens padrão ficam em bootable/recovery/res/images
. Para usar uma imagem estática
durante a instalação, só é necessário fornecer a imagem icon_installing.png e definir o número de
frames na animação para 0 (o ícone de erro não é animado, é sempre uma imagem estática).
Texto de recuperação localizado
O Android 5.x exibe uma string de texto (por exemplo, "Instalando atualização do sistema...") junto com o imagem. Quando o sistema principal inicializa na recuperação, ele passa a localidade atual do usuário como um opção de linha de comando para recuperação. Para cada mensagem exibida, a recuperação inclui uma segunda imagem composta com strings de texto pré-renderizadas para essa mensagem em cada localidade.
Exemplo de imagem de strings de texto de recuperação:
O texto de recuperação pode exibir as seguintes mensagens:
- Instalando atualização do sistema...
- Erro!
- Apagando... (ao fazer uma exclusão permanente de dados/redefinição para a configuração original)
- Nenhum comando (quando um usuário inicializa manualmente na recuperação)
O app Android no bootable/recovery/tools/recovery_l10n/
renderiza localizações
de uma mensagem e cria a imagem composta. Para saber como usar esse app, consulte a
comentários em
bootable/recovery/tools/recovery_l10n/src/com/android/recovery_l10n/Main.java
:
Quando um usuário inicializa a recuperação manualmente, a localidade pode não estar disponível e nenhum texto exibidos. Não torne as mensagens de texto essenciais para o processo de recuperação.
Observação: a interface oculta que exibe mensagens de registro e permite que o usuário a seleção de ações do menu está disponível apenas em inglês.
Barras de progresso
As barras de progresso podem aparecer abaixo da imagem (ou animação) principal. A barra de progresso é feita por combinar duas imagens de entrada, que precisam ser do mesmo tamanho:
A extremidade esquerda da imagem de preenchimento é exibida ao lado da extremidade direita do vazia para criar a barra de progresso. A posição do limite entre os dois imagens é alterado para indicar o progresso. Por exemplo, com os pares de imagens de entrada acima, exibir:
Você pode fornecer versões específicas para dispositivos dessas imagens colocando-as em (neste
exemplo) device/yoyodyne/tardis/recovery/res/images
, Os nomes dos arquivos devem corresponder aos listados acima. quando um arquivo é encontrado nesse diretório,
sistema de build a utiliza em vez da imagem padrão correspondente. Somente PNGs em RGB ou
O formato RGBA com profundidade de cor de 8 bits é aceito.
Observação:no Android 5.x, se a localidade for conhecida pela recuperação e for um idioma da direita para a esquerda (RTL) (árabe, hebraico etc.), a barra de progresso é preenchida da direita para sobrar mais
Dispositivos sem telas
Nem todos os dispositivos Android têm telas. Se o dispositivo for um dispositivo headless ou tiver um do usuário, talvez seja necessário personalizar mais a interface de recuperação. Em vez de criar uma subclasse de ScreenRecoveryUI, crie uma subclasse direta da classe pai RecoveryUI.
A RecoveryUI tem métodos para lidar com operações de interface de nível inferior, como "alternar a tela",
"atualizar a barra de progresso", "mostrar o cardápio", "mudar a seleção do menu", etc. Você pode substituir
para fornecer uma interface adequada para seu dispositivo. Talvez seu dispositivo tenha LEDs
você pode usar diferentes cores ou padrões de piscar para indicar o estado, ou talvez você possa tocar
áudio. (Talvez você não queira oferecer suporte a um menu ou modo de "exibição de texto". Você pode
impedir o acesso com CheckKey()
e
Implementações HandleMenuKey()
que nunca ativam a tela ou selecionam um menu
do item de linha. Nesse caso, muitos dos métodos RecoveryUI que você precisa fornecer podem ficar vazios
bate-papos?
Consulte bootable/recovery/ui.h
para a declaração de RecoveryUI para ver quais métodos
que você precisa oferecer. A RecoveryUI é abstrata. Alguns métodos são puramente virtuais e precisam ser fornecidos
mas contém o código para processar as entradas de chave. É possível substituir essa
também, se o dispositivo não tiver chaves ou se você quiser processá-las de forma diferente.
Updater
Você pode usar o código específico do dispositivo na instalação do pacote de atualização fornecendo seu próprias funções de extensão que podem ser chamadas de dentro do script do atualizador. Aqui está um exemplo para o dispositivo tardis:
device/yoyodyne/tardis/recovery/recovery_updater.c
#include <stdlib.h> #include <string.h> #include "edify/expr.h"
Todas as funções de extensão têm a mesma assinatura. Os argumentos são o nome pelo qual
foi chamada, um cookie State*
, o número de argumentos recebidos e uma
matriz de ponteiros Expr*
que representam os argumentos. O valor de retorno é um
Value*
recém-alocado.
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); }
Seus argumentos não foram avaliados no momento em que a função é chamada, ou seja,
a lógica determina quais deles são avaliados e quantas vezes. Assim, você pode usar extensões
funções para implementar suas próprias estruturas de controle. Call Evaluate()
para avaliar
um argumento Expr*
, retornando um Value*
. Se Evaluate()
retornar NULL, você deve liberar todos os recursos que estiver mantendo e retornar NULL imediatamente (esse
propaga é cancelado na pilha do edify). Caso contrário, você assume a propriedade do valor retornado e
são responsáveis por chamar
FreeValue()
nela.
Suponha que a função precise de dois argumentos: uma key com valor de string e um com valor de blob. image. Você poderia ler argumentos como este:
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; }
Verificar se há NULL e liberar argumentos avaliados anteriormente pode ser tedioso por várias
. A função ReadValueArgs()
pode facilitar isso. Em vez do código
acima, você poderia ter escrito isto:
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; }
O ReadValueArgs()
não faz a verificação de tipos, então você precisa fazer isso aqui. é mais
é conveniente fazer isso com uma instrução if ao custo de produzir uma resposta um pouco menos
uma mensagem de erro específica em caso de falha. Mas o ReadValueArgs()
processa a avaliação
de cada argumento e liberando todos os argumentos avaliados anteriormente (bem como definir uma
mensagem de erro) se alguma das avaliações falhar. Você pode usar um
Função de conveniência ReadValueVarArgs()
para avaliar um número variável de
(ele retorna uma matriz de Value*
).
Depois de avaliar os argumentos, execute o trabalho da função:
// 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 ...
O valor de retorno precisa ser um objeto Value*
. a propriedade deste objeto será transferida para
o autor da chamada. O autor da chamada assume a propriedade de todos os dados para os quais ele aponta
Value*
: especificamente o datamember.
Nesse caso, você quer retornar um valor verdadeiro ou falso para indicar o sucesso. Lembre-se do
convenção de que a string vazia é false e todas as outras strings são true. Você
precisa inserir um objeto de valor com uma cópia maliciosa da string constante a ser retornada, já que o
autor da chamada usará free()
em ambos. Não se esqueça de chamar FreeValue()
no
objetos obtidos ao avaliar seus 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; }
A função de conveniência StringValue()
encapsula uma string em um novo objeto Value.
Use para escrever o código acima de forma mais sucinta:
FreeValue(key); FreeValue(image); return StringValue(strdup(successful ? "t" : "")); }
Para conectar funções ao intérprete do edify, forneça a função
Register_foo
, em que foo é o nome da biblioteca estática que contém
para esse código. Chame RegisterFunction()
para registrar cada função de extensão. De
convenção, nomeie funções específicas do dispositivo como device.whatever
para evitar
entra em conflito com futuras funções integradas adicionadas.
void Register_librecovery_updater_tardis() { RegisterFunction("tardis.reprogram", ReprogramTardisFn); }
Agora você pode configurar o makefile para criar uma biblioteca estática com seu código. (Isto é o mesmo makefile usado para personalizar a interface de recuperação na seção anterior; o dispositivo pode ter bibliotecas estáticas definidas aqui.
device/yoyodyne/tardis/recovery/Android.mk
include $(CLEAR_VARS) LOCAL_SRC_FILES := recovery_updater.c LOCAL_C_INCLUDES += bootable/recovery
O nome da biblioteca estática precisa corresponder ao nome da
Register_libname
contida nele.
LOCAL_MODULE := librecovery_updater_tardis include $(BUILD_STATIC_LIBRARY)
Por fim, configure a compilação de recuperação para extrair sua biblioteca. Adicionar sua biblioteca a
TARGET_RECOVERY_UPDATER_LIBS (que pode conter várias bibliotecas; todas são registradas).
Se o código depender de outras bibliotecas estáticas que não sejam extensões do Education (por exemplo,
não tiverem uma função Register_libname
), liste-os em
TARGET_RECOVERY_UPDATER_EXTRA_LIBS para vinculá-los ao atualizador sem chamar
(inexistente). Por exemplo, se o código específico do dispositivo quisesse usar
zlib para descompactar dados, inclua libz aqui.
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 +=
Os scripts de atualizador no pacote OTA agora podem chamar sua função como qualquer outro. Para reprogramar
seu dispositivo tardis, o script de atualização poderá conter:
tardis.reprogram("the-key", package_extract_file("tardis-image.dat"))
: Isso usa
a versão de argumento único da função integrada package_extract_file()
;
que retorna o conteúdo de um arquivo extraído do pacote de atualização como um blob para produzir
o segundo argumento para a nova função de extensão.
Geração de pacotes OTA
O último componente é fazer com que as ferramentas de geração de pacotes OTA conheçam seu dados específicos do dispositivo e emitem scripts de atualizador que incluem chamadas para as funções de extensão.
Primeiro, faça com que o sistema de compilação saiba sobre um blob de dados específico do dispositivo. Supondo que seus dados
arquivo estiver em device/yoyodyne/tardis/tardis.dat
, declare o seguinte no seu
AndroidBoard.mk do dispositivo:
device/yoyodyne/tardis/AndroidBoard.mk
[...] $(call add-radio-file,tardis.dat)
Você também pode colocá-los em um Android.mk, mas precisa ser protegido por um dispositivo uma vez que todos os arquivos Android.mk na árvore são carregados, independentemente do dispositivo construído. Se sua árvore incluir vários dispositivos, convém adicionar apenas o arquivo tardis.dat ao a criação do 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
Eles são chamados de arquivos de rádio por motivos históricos; elas podem não ter nada a ver com
rádio do dispositivo (se houver). Eles são simplesmente blobs de dados opacos que o sistema de build copia para o
o target-files .zip usado pelas ferramentas de geração OTA. Quando você cria um build, o tardis.dat é
armazenado no target-files.zip como RADIO/tardis.dat
. Você pode ligar
add-radio-file
várias vezes para adicionar quantos arquivos você quiser.
Módulo do Python
Para estender as ferramentas de lançamento, escreva um módulo Python (deve ser nomeado releasetools.py) das ferramentas pode chamar se estiver presente. Exemplo:
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"));""")
Uma função separada lida com o caso da geração de um pacote OTA incremental. Para isso Por exemplo, suponha que você precise reprogramar o tardis somente quando o arquivo tardis.dat tiver mudado entre dois builds.
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"));""")
Funções do módulo
Você pode fornecer as seguintes funções no módulo (implemente apenas as que forem necessárias).
FullOTA_Assertions()
- Chamado perto do início da geração de uma OTA completa. Esse é um bom lugar para emitir declarações sobre o estado atual do dispositivo. Não emita comandos de script que fazem alterações no dispositivo.
FullOTA_InstallBegin()
- Chamado depois que todas as declarações sobre o estado do dispositivo tiverem passado, mas antes de qualquer mudança foram feitas. É possível emitir comandos para atualizações específicas do dispositivo que precisam ser executadas antes qualquer outra coisa no dispositivo foi alterada.
FullOTA_InstallEnd()
- Chamado no final da geração do script, depois dos comandos do script para atualizar a inicialização e partições do sistema foram emitidas. Também é possível emitir comandos adicionais para e atualizações específicas para cada dispositivo.
IncrementalOTA_Assertions()
-
Semelhante a
FullOTA_Assertions()
, mas chamado ao gerar um valor incremental update. IncrementalOTA_VerifyBegin()
- Chamado após todas as declarações sobre o estado do dispositivo terem passado, mas antes que qualquer mudança tenha sido ter sido feita. Você pode emitir comandos para atualizações específicas do dispositivo que precisam ser executados antes de qualquer ou outro no dispositivo foi alterado.
IncrementalOTA_VerifyEnd()
- Chamado no final da fase de verificação, quando o script termina de confirmar a os arquivos que ele vai tocar têm o conteúdo inicial esperado. Neste ponto, nada na dispositivo foi alterado. Também é possível emitir códigos para outros comandos verificações.
IncrementalOTA_InstallBegin()
- Chamado depois que os arquivos a serem corrigidos têm o erro esperado antes do estado, mas antes de qualquer mudança ser feita. É possível emitir comandos para atualizações específicas do dispositivo que precisam ser executadas antes que qualquer outra coisa no dispositivo seja alterada.
IncrementalOTA_InstallEnd()
- Semelhante ao pacote OTA completo, ele é chamado no final do script. geração de registros, depois que os comandos de script para atualizar as partições de inicialização e sistema foram emitidos. Também é possível emitir comandos adicionais para atualizações específicas do dispositivo.
Observação:se o dispositivo ficar sem energia, a instalação OTA poderá ser reiniciada no desde o início. Prepare-se para lidar com dispositivos em que esses comandos já foram executados. total ou parcialmente.
Transmitir funções para objetos de informação
Transmita funções para um único objeto de informação que contenha vários itens úteis:
-
info.input_zip. (Somente OTAs completas) O objeto
zipfile.ZipFile
do de entrada target-files .zip. -
info.source_zip. (Somente OTAs incrementais) O objeto
zipfile.ZipFile
para o arquivo .zip de origem de destino (o build já estava no dispositivo quando o pacote incremental está sendo instalado). -
info.target_zip. (Somente OTAs incrementais) O objeto
zipfile.ZipFile
para target-files .zip (a compilação que o pacote incremental coloca no dispositivo). -
info.output_zip. O pacote está sendo criado. um objeto
zipfile.ZipFile
foi aberto para escrever. Use common.ZipWriteStr(info.output_zip, filename, data) para adicionar um no pacote. -
info.script. Objeto de script ao qual você pode anexar comandos. Ligação
info.script.AppendExtra(script_text)
para gerar texto no script. Verifique se o texto de saída termina com ponto e vírgula para que não execute comandos emitidos depois.
Para detalhes sobre o objeto de informações, consulte Documentação da Python Software Foundation para arquivos ZIP (em inglês).
Especificar o local do módulo
Especifique a localização do script releasetools.py do dispositivo no arquivo BoardConfig.mk:
device/yoyodyne/tardis/BoardConfig.mk
[...] TARGET_RELEASETOOLS_EXTENSIONS := device/yoyodyne/tardis
Se TARGET_RELEASETOOLS_EXTENSIONS não está definida, o padrão é
Diretório $(TARGET_DEVICE_DIR)/../common
(device/yoyodyne/common
neste exemplo). É melhor definir explicitamente o local do script "releasetools.py".
Ao criar o dispositivo tardis, o script "releasetools.py" é incluído nos arquivos de destino
Arquivo .zip (META/releasetools.py
).
Quando você executa as ferramentas de lançamento (img_from_target_files
ou
ota_from_target_files
), o script Releasetools.py no arquivo .zip de target-files, se
presente, é preferível em relação à da árvore de origem do Android. Também é possível definir
especifique o caminho para as extensões específicas do dispositivo com o -s
(ou
--device_specific
), que tem a prioridade máxima. Isso permite que você
corrigir os erros, fazer alterações nas extensões Releasetools e aplicar essas alterações às
target-files.
Agora, quando você executar ota_from_target_files
, ele vai coletar automaticamente
módulo específico do dispositivo do arquivo .zip target_files e o utiliza ao gerar dados OTA
pacotes:
./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, você pode especificar extensões específicas do dispositivo ao executar
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
Observação: para uma lista completa de opções, consulte
ota_from_target_files
comentários em
build/make/tools/releasetools/ota_from_target_files
.
Mecanismo de sideload
A recuperação tem um mecanismo de sideload para instalar manualmente um pacote de atualização sem fazendo o download over the air pelo sistema principal. O sideload é útil para depurar ou criar mudanças nos dispositivos em que não é possível inicializar o sistema principal.
Historicamente, o sideload é feito com o carregamento de pacotes fora do cartão SD do dispositivo. no no caso de um dispositivo que não seja de inicialização, o pacote pode ser colocado no cartão SD usando algum outro computador e o cartão SD inserido no dispositivo. Para acomodar dispositivos Android sem armazenamento externo removível, a recuperação suporta dois outros mecanismos de sideload: carregar pacotes da partição de cache e carregá-los via USB usando o adb.
Para invocar cada mecanismo de sideload, o método Device::InvokeMenuItem()
do dispositivo
pode retornar os seguintes valores de EmbedinAction:
-
APPLY_EXT. Transferir um pacote de atualização do armazenamento externo por sideload (
/sdcard
). O "recovery.fstab" precisa definir o ponto de montagem/sdcard
. Isso é não pode ser usado em dispositivos que emulam um cartão SD com um link simbólico para/data
(ou alguns mecanismo similar). Normalmente, o/data
não está disponível para recuperação porque está podem ser criptografados. A interface de recuperação mostra um menu de arquivos ZIP em/sdcard
e permite que o usuário selecione um. -
APLICAR_CACHE. É semelhante ao carregamento de um pacote do
/sdcard
, mas o O diretório/cache
(que está sempre disponível para recuperação) é usado. como alternativa. No sistema normal,/cache
só é gravável por usuários privilegiados, Se o dispositivo não for inicializável, o diretório/cache
não poderá ser gravado (o que torna esse mecanismo de utilidade limitada). -
APPLY_ADB_SIDELOAD. Permite que o usuário envie um pacote para o dispositivo por meio de um cabo USB e
a ferramenta de desenvolvimento adb. Quando esse mecanismo é invocado, a recuperação inicia seu próprio mini
do daemon do adbd para permitir que o adb em um computador host conectado se comunique com ele. Este mini
compatível com apenas um comando:
adb sideload filename
. O arquivo nomeado é enviado da máquina host para o dispositivo, que verifica e o instala como se estivesse no armazenamento local.
Algumas ressalvas:
- Somente o transporte USB é aceito.
-
Se a recuperação executar o adbd normalmente (geralmente verdadeiro para builds userdebug e eng), isso será
será desligado enquanto o dispositivo estiver no modo de sideload adb e será reiniciado quando o adb
sideload terminou de receber um pacote. No modo de sideload do adb, nenhum comando adb outro
do que
sideload
funcionam (logcat
,reboot
,push
,pull
,shell
etc. falham). -
Não é possível sair do modo de sideload do adb no dispositivo. Para cancelar, envie:
/dev/null
(ou qualquer outra coisa que não seja um pacote válido) como o pacote, e o dispositivo não o verificará e interromperá o procedimento de instalação. RecoveryUI o métodoCheckKey()
da implementação vai continuar sendo chamado para pressionamentos de tecla, para que você possa fornecer uma sequência de teclas que reinicia o dispositivo e funciona no modo de sideload do adb.