Checkpoint de dados do usuário

O Android 10 apresenta o Checkpoint de dados do usuário (UDC, na sigla em inglês), que permite que o Android seja revertido ao estado anterior quando uma atualização OTA falha. Com o UDC, se uma atualização OTA do Android falhar, o dispositivo poderá ser revertido com segurança para o estado anterior. Embora atualizações A/B resolvam esse problema para inicialização antecipada, não há suporte para reversão quando a partição de dados do usuário (montada em /data) é modificada.

O UDC permite que o dispositivo reverta a partição de dados do usuário mesmo depois de ser modificado. O recurso UDC faz isso com recursos de ponto de verificação no sistema de arquivos, uma implementação alternativa quando o sistema de arquivos não oferece suporte a pontos de verificação, integração com o mecanismo A/B do carregador de inicialização, além de oferecer suporte a atualizações não A/B e suporte para vinculação de versão de chave e prevenção de reversão de chave.

Impacto no usuário

O recurso UDC melhora a experiência de atualização OTA para os usuários, já que menos usuários perdem os dados quando uma atualização OTA falha. Isso pode reduzir o número de chamadas de suporte de usuários que enfrentam problemas durante o processo de atualização. No entanto, quando uma atualização OTA falha, os usuários podem notar que o dispositivo é reinicializado várias vezes.

Como funciona

Funcionalidade de checkpoint em diferentes sistemas de arquivos

Para o sistema de arquivos F2FS, o UDC adiciona a funcionalidade de ponto de verificação ao kernel Linux 4.20 upstream e a envia de volta para todos os kernels comuns aceitos por dispositivos que executam o Android 10.

Para outros sistemas de arquivos, o UDC usa um dispositivo virtual de mapeamento de dispositivo chamado dm_bow para a funcionalidade de ponto de verificação. O dm_bow fica entre o dispositivo e o sistema de arquivos. Quando uma partição é montada, um trim é emitido, fazendo com que o sistema de arquivos emita comandos de trim em todos os blocos livres. O dm_bow intercepta esses cortes e os usa para configurar uma lista de bloqueio sem custo financeiro. As leituras e gravações são enviadas ao dispositivo sem modificações, mas antes que uma gravação seja permitida, os dados necessários para uma restauração são armazenados em um bloco livre.

Processo de checkpoint

Quando uma partição com a flag checkpoint=fs/block é montada, o Android chama restoreCheckpoint na unidade para permitir que o dispositivo restaure qualquer checkpoint atual. init chama a função needsCheckpoint para determinar se o dispositivo está em um estado de carregador de inicialização A/B ou se definiu a contagem de tentativas de atualização. Se uma delas for verdadeira, o Android vai chamar createCheckpoint para adicionar flags de montagem ou criar um dispositivo dm_bow.

Depois que a partição é montada, o código de ponto de verificação é chamado para emitir cortes. O processo de inicialização continua normalmente. Em LOCKED_BOOT_COMPLETE, o Android chama commitCheckpoint para confirmar o checkpoint atual, e a atualização continua normalmente.

Gerenciar chaves do keymaster

As chaves Keymaster são usadas para criptografia de dispositivos ou outras finalidades. Para gerenciar essas chaves, o Android atrasa as chamadas de exclusão de chaves até que o ponto de verificação seja confirmado.

Monitorar a integridade

Um daemon de integridade verifica se há espaço suficiente em disco para criar um ponto de verificação. O daemon de integridade está localizado em cp_healthDaemon em Checkpoint.cpp.

O daemon de integridade tem os seguintes comportamentos que podem ser configurados:

APIs de ponto de verificação

As APIs de ponto de verificação são usadas pelo recurso UDC. Para outras APIs usadas pela UDC, consulte IVold.aidl.

void startCheckpoint(int retry)

Cria um ponto de verificação.

O framework chama esse método quando está pronto para iniciar uma atualização. O checkpoint é criado antes que sistemas de arquivos com checkpoint, como userdata, sejam montados em R/W após a reinicialização. Se a contagem de novas tentativas for positiva, a API vai processar as novas tentativas de rastreamento, e o atualizador vai chamar needsRollback para verificar se é necessário fazer um rollback da atualização. Se a contagem de novas tentativas for -1, a API vai adiar o julgamento do carregador de inicialização A/B.

Esse método não é chamado ao fazer uma atualização A/B normal.

void commitChanges()

Faz o commit das alterações.

O framework chama esse método após a reinicialização, quando as mudanças estão prontas para serem confirmadas. Isso é chamado antes que os dados (como imagens, vídeos, SMS, recibo de servidor) sejam gravados em userdata e antes de BootComplete.

Se não houver uma atualização ativa com checkpoint, esse método não terá efeito.

abortChanges()

Força a reinicialização e reverte para o ponto de verificação. Abandona todas as modificações de userdata desde a primeira reinicialização.

O framework chama esse método após a reinicialização, mas antes de commitChanges. retry_counter é diminuído quando esse método é chamado. As entradas de registro são geradas.

bool needsRollback()

Determina se uma reversão é necessária.

Em dispositivos sem ponto de verificação, retorna false. Em dispositivos de checkpoint, retorna true durante uma inicialização sem checkpoint.

Implementar a UDC

Implementação de referência

Para conferir um exemplo de como a UDC pode ser implementada, consulte dm-bow.c. Para mais documentação sobre o recurso, consulte dm-bow.txt.

Configurar

Em on fs no arquivo init.hardware.rc, verifique se você tem:

mount_all /vendor/etc/fstab.${ro.boot.hardware.platform} --early

Em on late-fs no arquivo init.hardware.rc, verifique se você tem:

mount_all /vendor/etc/fstab.${ro.boot.hardware.platform} --late

No arquivo fstab.hardware, verifique se /data está marcado como latemount.

/dev/block/bootdevice/by-name/userdata              /data              f2fs
noatime,nosuid,nodev,discard,reserve_root=32768,resgid=1065,fsync_mode=nobarrier
latemount,wait,check,fileencryption=ice,keydirectory=/metadata/vold/metadata_encryption,quota,formattable,sysfs_path=/sys/devices/platform/soc/1d84000.ufshc,reservedsize=128M,checkpoint=fs

Adicionar partição de metadados

A UDC exige uma partição de metadados para armazenar a contagem de novas tentativas de não inicialização e as chaves. Configure uma partição de metadados e monte-a em /metadata.

No arquivo fstab.hardware, verifique se /metadata está marcado como earlymount ou first_stage_mount.

/dev/block/by-name/metadata           /metadata           ext4
noatime,nosuid,nodev,discard,sync
wait,formattable,first_stage_mount

Inicializa a partição com todos os zeros.

Adicione as linhas abaixo a BoardConfig.mk:

BOARD_USES_METADATA_PARTITION := true
BOARD_ROOT_EXTRA_FOLDERS := existing_folders metadata

Atualizar sistemas

Sistemas F2FS

Para sistemas que usam o F2FS para formatar dados, verifique se a versão do F2FS oferece suporte a pontos de verificação. Para mais informações, consulte Funcionalidade de ponto de verificação em diferentes sistemas de arquivos.

Adicione a flag checkpoint=fs à seção <fs_mgr_flags> do fstab para o dispositivo montado em /data.

Sistemas que não são F2FS

Para sistemas que não são F2FS, o dm-bow precisa ser ativado na configuração do kernel.

Adicione a flag checkpoint=block à seção <fs_mgr_flags> do fstab para o dispositivo montado em /data.

Verificar os registros

As entradas de registro são geradas quando as APIs de Checkpoint são chamadas.

Validação

Para testar a implementação do UDC, execute o conjunto VtsKernelCheckpointTest de testes VTS.