O Android 12 oferece suporte à passagem FUSE, que minimiza a sobrecarga do FUSE para alcançar um desempenho comparável ao acesso direto ao sistema de arquivos inferior. A transmissão direta do FUSE é compatível com os kernels android12-5.4
,
android12-5.10
e android-mainline
(somente para testes). Isso significa
que a compatibilidade com esse recurso depende do kernel usado pelo dispositivo e da
versão do Android em execução:
Os dispositivos que fazem upgrade do Android 11 para o Android 12 não podem oferecer suporte à transferência direta do FUSE porque os kernels desses dispositivos estão congelados e não podem ser movidos para um kernel que foi oficialmente atualizado com as mudanças de transferência direta do FUSE.
Os dispositivos lançados com o Android 12 podem oferecer suporte à transferência direta do FUSE ao usar um kernel oficial. Para esses dispositivos, o código do framework Android que implementa a transmissão direta do FUSE está incorporado ao módulo principal MediaProvider, que é atualizado automaticamente. Dispositivos que não implementam o MediaProvider como um módulo principal (por exemplo, dispositivos Android Go) também podem acessar mudanças no MediaProvider à medida que são compartilhadas publicamente.
FUSE x SDCardFS
O sistema de arquivos no espaço do usuário (FUSE) é um mecanismo que permite que as operações realizadas em um sistema de arquivos FUSE sejam terceirizadas pelo kernel (driver FUSE) para um programa do espaço do usuário (daemon FUSE), que implementa as operações. O Android 11 descontinuou o SDCardFS e tornou o FUSE a solução padrão para emulação de armazenamento. Como parte dessa mudança, o Android implementou o próprio daemon FUSE para interceptar acessos a arquivos, aplicar recursos extras de segurança e privacidade e manipular arquivos durante a execução.
Embora o FUSE tenha um bom desempenho ao lidar com informações armazenáveis em cache, como páginas ou atributos, ele introduz regressões de desempenho ao acessar o armazenamento externo, que são especialmente visíveis em dispositivos de gama média e baixa. Essas regressões são causadas por uma cadeia de componentes que cooperam na implementação do sistema de arquivos FUSE, bem como por várias trocas do espaço do kernel para o espaço do usuário em comunicações entre o driver FUSE e o daemon FUSE (em comparação com o acesso direto ao sistema de arquivos inferior, que é mais simples e totalmente implementado no kernel).
Para mitigar essas regressões, os apps podem usar a junção para reduzir a cópia de dados e usar a API ContentProvider para ter acesso direto aos arquivos do sistema de arquivos de nível inferior. Mesmo com essas e outras otimizações, as operações de leitura e gravação podem ter largura de banda reduzida ao usar o FUSE em comparação com o acesso direto ao sistema de arquivos inferior, especialmente com operações de leitura aleatória, em que nenhum armazenamento em cache ou leitura antecipada pode ajudar. Além disso, os apps que acessam o armazenamento diretamente pelo caminho legado /sdcard/
continuam apresentando quedas perceptíveis de desempenho, principalmente ao realizar operações com uso intenso de E/S.
Solicitações de espaço do usuário do SDcardFS
Usar o SDcardFS pode acelerar a emulação de armazenamento e as verificações de permissão do FUSE removendo a chamada de espaço do usuário do kernel. As solicitações do espaço do usuário seguem o caminho: espaço do usuário → VFS → sdcardfs → VFS → ext4 → cache de página/armazenamento.
Figura 1. Solicitações de espaço do usuário do SDcardFS
Solicitações do espaço do usuário do FUSE
Inicialmente, o FUSE era usado para ativar a emulação de armazenamento e permitir que os apps usassem de forma transparente o armazenamento interno ou um cartão SD externo. O uso do FUSE introduz alguma sobrecarga porque cada solicitação do espaço do usuário segue o caminho: espaço do usuário → VFS → driver FUSE → daemon FUSE → VFS → ext4 → cache de página/armazenamento.
Figura 2. Solicitações do espaço do usuário do FUSE
Solicitações de transferência direta do FUSE
A maioria das permissões de acesso a arquivos é verificada quando o arquivo é aberto, e outras verificações de permissão ocorrem ao ler e gravar nesse arquivo. Em alguns casos, é possível saber no momento da abertura do arquivo que o app solicitante tem acesso total ao arquivo solicitado. Assim, o sistema não precisa continuar encaminhando leitura e gravação das solicitações do driver FUSE para o daemon FUSE (já que isso apenas moveria dados de um lugar para outro).
Com a transferência direta do FUSE, o daemon do FUSE que processa uma solicitação aberta pode notificar o driver do FUSE de que a operação é permitida e que todas as solicitações de leitura e gravação subsequentes podem ser encaminhadas diretamente para o sistema de arquivos inferior. Isso evita o excesso de espera para que o daemon FUSE do espaço do usuário responda às solicitações do driver FUSE.
Confira abaixo uma comparação entre solicitações de FUSE e de transferência direta do FUSE.
Figura 3. Solicitação do FUSE x solicitação de transferência direta do FUSE
Quando um app realiza um acesso ao sistema de arquivos FUSE, as seguintes operações ocorrem:
O driver FUSE processa e enfileira a solicitação, apresentando-a ao daemon FUSE que processa esse sistema de arquivos FUSE por uma instância de conexão específica no arquivo
/dev/fuse
, que o daemon FUSE não pode ler.Quando o daemon FUSE recebe uma solicitação para abrir um arquivo, ele decide se o encaminhamento FUSE deve estar disponível para esse arquivo específico. Se ele estiver disponível, o daemon:
Notifica o driver FUSE sobre essa solicitação.
Ativa a transferência FUSE para o arquivo usando o ioctl
FUSE_DEV_IOC_PASSTHROUGH_OPEN
, que precisa ser executado no descritor de arquivo do/dev/fuse
aberto.
O ioctl recebe (como um parâmetro) uma estrutura de dados que contém o seguinte:
Descritor de arquivo do arquivo do sistema de arquivos inferior que é o destino do recurso de passagem.
Identificador exclusivo da solicitação FUSE que está sendo processada no momento (precisa estar aberta ou ser do tipo "criar e abrir").
Campos extras que podem ser deixados em branco e são destinados a implementações futuras.
Se o ioctl for bem-sucedido, o daemon FUSE vai concluir a solicitação de abertura, o driver FUSE vai processar a resposta do daemon FUSE, e uma referência ao arquivo do sistema de arquivos inferior será adicionada ao arquivo FUSE no kernel. Quando um app solicita uma operação de leitura/gravação em um arquivo FUSE, o driver FUSE verifica se a referência a um arquivo do sistema de arquivos inferior está disponível.
Se uma referência estiver disponível, o driver vai criar uma nova solicitação do sistema de arquivos virtual (VFS) com os mesmos parâmetros direcionados ao arquivo do sistema de arquivos inferior.
Se uma referência não estiver disponível, o driver encaminhará a solicitação para o daemon FUSE.
As operações acima ocorrem para leitura/gravação e leitura-iter/gravação-iter em arquivos genéricos e operações de leitura/gravação em arquivos mapeados na memória. O encaminhamento direto do FUSE para um determinado arquivo existe até que ele seja fechado.
Implementar a visualização externa do FUSE
Para ativar a transferência direta de FUSE em dispositivos com Android
12, adicione as seguintes linhas ao arquivo
$ANDROID_BUILD_TOP/device/…/device.mk
do dispositivo de destino.
# Use FUSE passthrough
PRODUCT_PRODUCT_PROPERTIES += \
persist.sys.fuse.passthrough.enable=true
Para desativar a transmissão direta do FUSE, omita a mudança de configuração acima ou defina
persist.sys.fuse.passthrough.enable
como false
. Se você já tiver ativado a transferência direta do FUSE, desativá-la vai impedir que o dispositivo use esse recurso, mas ele vai continuar funcionando.
Para ativar/desativar a transferência direta do FUSE sem atualizar o dispositivo, mude a propriedade do sistema usando comandos ADB. Veja um exemplo abaixo.
adb root
adb shell setprop persist.sys.fuse.passthrough.enable {true,false}
adb reboot
Para mais ajuda, consulte a implementação de referência.
Validar a transmissão direta do FUSE
Para validar se o MediaProvider está usando a transmissão direta do FUSE, verifique logcat
para
mensagens de depuração. Exemplo:
adb logcat FuseDaemon:V \*:S
--------- beginning of main
03-02 12:09:57.833 3499 3773 I FuseDaemon: Using FUSE passthrough
03-02 12:09:57.833 3499 3773 I FuseDaemon: Starting fuse...
A entrada FuseDaemon: Using FUSE passthrough
no registro garante que a transmissão direta do FUSE
está em uso.
O CTS do Android 12 inclui CtsStorageTest
, que
inclui testes que acionam a transmissão FUSE. Para executar o teste manualmente, use
atest, conforme mostrado abaixo:
atest CtsStorageTest