Introdução ao funcionamento interno do Android
- por Sergio Prado
O Android está na moda. E não estamos falando de nenhum novo robozinho da Sony ou da Honda. Estamos falando do sistema operacional do Google (ou da Google, como preferirem), desenvolvido especialmente para dispositivos móveis.
Como é um sistema aberto (apesar de não ter necessariamente todo código-fonte disponível), todos querem embarcar o Android em seus dispositivos e se aproveitar de toda a infraestrutura de aplicações e serviços disponíveis no mercado.
Uma característica que ajuda na escolha deste sistema operacional é que ele usa o kernel do Linux. Mas o que isso significa? O Android é uma distribuição Linux? Como ele funciona internamente? É isso que veremos nesta série de artigos sobre a arquitetura interna do Android.
HISTÓRICO
Em 2005, uma empresa chamada Android Inc., que desenvolvia um sistema operacional móvel de mesmo nome, foi comprada pelo Google. Com a popularização do uso de smartphones trazida pelo iPhone da Apple, o Google sentiu que poderia expandir seu negócio de venda de anúncios também em dispositivos móveis.
Em 2007, para cuidar do projeto Android, o Google criou a Open Handset Alliance, um consórcio de empresas de tecnologia envolvidas no mercado de dispositivos móveis (HTC, Sony, Intel, Motorola, Sansumg, etc). No fim deste mesmo ano, é liberada a primeira versão do Android, juntamente com o SDK para o desenvolvimento de aplicações.
ARQUITETURA BÁSICA
O Android é um sistema operacional baseado no kernel do Linux. Apesar de ter sido desenvolvido inicialmente para smartphones, hoje é usado em diversas outras aplicações como tablets e até relógios.
Apesar de ser baseado no kernel do Linux, existe pouca coisa em comum com distribuições Linux convencionais (embarcadas ou não). À grosso modo, o Android é uma máquina virtual Java rodando sobre o kernel do Linux, dando suporte para o desenvolvimento de aplicações Java através de um conjunto de bibliotecas e serviços. Sua arquitetura tem basicamente 4 camadas:
Linux kernel: o Android usa o kernel do Linux com alguns patchs, que adicionam algumas funcionalidades através de módulos do kernel. Veremos mais adiante quais são estas funcionalidades.
Bibliotecas e serviços: aqui estão as bibliotecas básicas do sistema como a Bionic, a OpenGL/ES para trabalhar com gráficos, e a SQLite para trabalhar com banco de dados. Aqui também estão os serviços providos para as camadas superiores, incluindo a máquina virtual Java (Dalvik). A maior parte destas bibliotecas e serviços estão desenvolvidos em C e C++.
Framework: esta camada é desenvolvida quase toda em Java, e faz a interface com as aplicações Android. Ela provê um conjunto de bibliotecas para acessar os diversos recursos do dispositivo como interface gráfica, telefonia, localizador (GPS), banco de dados persistente, armazenamento no cartão SD, etc.
Aplicações: é aqui que ficam as aplicações (desenvolvidas em Java) para o Android. E é um dos grandes segredos do sucesso da plataforma, já que possui mais de 250.000 aplicações no Android Market, e continua crescendo cada dia que passa.
Para uma descrição mais completa da arquitetura do Android, dê uma olhada no guia de desenvolvimento Android disponibilizado pelo Google.
Parece simples, não? Nem tanto assim. Se olharmos para a arquitetura interna do Android, veremos o nível de complexidade deste sistema operacional:
Realmente muita coisa acontece quando rodamos uma simples aplicação. Vamos então tentar entender melhor tudo isso?
O KERNEL
Vimos que o Android usa uma versão modificada do kernel do Linux. Dentre as principais modificações, temos:
binder: sabemos que em todo sistema operacional com suporte à memória virtual, os processos rodam em diferentes regiões de memória. Isso significa que nenhum processo tem acesso à região de memória de outro processo. E por isso precisamos de um mecanismo de comunicação entre processos. Mas quem esta acostumado com o padrão System V IPC usado em sistemas Linux tradicionais para comunicação entre processos (message queues, semáforos e shared memory) vai precisar “voltar à escola”. O Android usa o Binder para a comunicação entre processos. Ele implementa um modulo no kernel em “drivers/misc/binder.c” para esta tarefa. Toda comunicação entre processos no Android passa pelo binder. Para o desenvolvedor de aplicações Android, o processo é transparente, já que é abstraído pelas bibliotecas do sistema.
ashmem: um novo mecanismo de compartilhamento de memória, onde dois processos podem se comunicar através desta região compartilhada de memória. É mais leve e simples de usar, e tem melhor suporte a dispositivos com pouca memória, já que tem a capacidade de descartar regiões de memória compartilhada de maneira segura em caso de pouca memória disponível. Sua implementação encontra-se em “mm/ashmem.c“.
logger: o Android possui um sistema global de logs, implementado através de um módulo do kernel. Ele cria 4 arquivos de dispositivo em “/dev/log”, cada um representando um buffer diferente:
# ls -l /dev/log crw-rw--w- root log 10, 54 1970-01-01 00:00 system crw-rw--w- root log 10, 55 1970-01-01 00:00 radio crw-rw--w- root log 10, 56 1970-01-01 00:00 events crw-rw--w- root log 10, 57 1970-01-01 00:00 main |
Para as aplicações acessarem o sistema de log, basta abrir e ler ou escrever num destes arquivos de dispositivo. A implementação deste módulo no kernel encontra-se em “drivers/misc/logger.c“.
wakelocks: se um dispositivo Android ficar um tempo sem ser usado, entrará em modo de baixo consumo para garantir economia de bateria. O módulo de wakelock permite que as aplicações desabilitem o mecanismo de baixo consumo. Por exemplo, se você precisar executar um processo em background que não pode ser interrompido para entrar em modo de baixo consumo, este módulo possibilita a desativação temporária deste recurso até que seu processo finalize a execução. Sua implementação encontra-se em “kernel/power/wakelock.c“.
oom handling: implementado em “drivers/misc/lowmemorykiller.c“, controla o uso de memória do sistema e mata processos se verificar que a memória disponível esta abaixo de um valor mínimo aceitável.
timed GPIO: possibilita acionar saídas de I/O de forma temporizada. Está implementado em “drives/misc/timed_gpio.c“.
O SISTEMA DE ARQUIVOS
Esta é a árvore de diretórios de um sistema de arquivos para o Android:
$ sudo tree -d -L 2 . . |-- acct | `-- uid |-- cache | `-- lost+found |-- config |-- d -> /sys/kernel/debug |-- data | |-- anr | |-- app | |-- app-private | |-- backup | |-- bootchart | |-- dalvik-cache | |-- data | |-- dontpanic | |-- local | |-- lost+found | |-- misc | |-- property | |-- secure | |-- system | `-- tombstones |-- dev |-- mnt | |-- asec | |-- sdcard | `-- secure |-- part-3 |-- proc |-- sbin |-- sys `-- system |-- app |-- bin |-- etc |-- fonts |-- framework |-- lib |-- media |-- ti-dsp |-- usr `-- xbin |
Bem diferente de um sistema Linux convencional, não é verdade? Os dois principais diretórios são o “data“, que armazena os dados das aplicações, e o “system“, com as bibliotecas (system/lib), serviços (system/bin e system/xbin) e aplicações Java (system/app).
E por falar em bibliotecas do sistema, nada de glibc ou uClibc. O Android implementou uma biblioteca chamada Bionic para usar como biblioteca do sistema. Porque? Me parece que o Google tem algum problema com licenças GPL, e eles tiraram do user space todo e qualquer software com licença GPL. A Bionic usa a licensa BSD, e suporta as arquiteturas x86 e ARM.
Agora, se alguém do Google esta lendo este artigo, por favor me respondam, porque vocês não usaram o Busybox??? Bom, tenho quase certeza de que também tem a ver com a licensa GPL do Busybox. De qualquer forma, o Android usa o Toolbox, uma implementação no mesmo esquema do Busybox, que também traz um conjunto (mais limitado) de comandos e ferramentas úteis para gerenciar um sistema Android. Repare nos executáveis que fazem um link para o toolbox na listagem abaixo:
$ ls -l /system/bin lrwxrwxrwx system system 2011-08-13 04:24 reboot -> toolbox -rwxr-xr-x system system 9648 2011-08-13 04:24 audioloop -rwxr-xr-x system system 100916 2011-08-13 04:24 iptables -rwxr-xr-x system system 9748 2011-08-13 04:24 sdcard lrwxrwxrwx system system 2011-08-13 04:24 ps -> toolbox lrwxrwxrwx system system 2011-08-13 04:24 top -> toolbox -rw-r--r-- system system 5372 2011-08-13 04:24 testwrap lrwxrwxrwx system system 2011-08-13 04:24 mount -> toolbox -rwxr-xr-x system system 5548 2011-08-13 04:24 dvz -rwxr-xr-x system system 18112 2011-08-13 04:24 debuggerd lrwxrwxrwx system system 2011-08-13 04:24 ln -> toolbox lrwxrwxrwx system system 2011-08-13 04:24 ifconfig -> toolbox lrwxrwxrwx system system 2011-08-13 04:24 renice -> toolbox -rwxr-xr-x system system 10792 2011-08-13 04:24 showlease drwxr-xr-x system system 2011-08-13 04:24 sgx lrwxrwxrwx system system 2011-08-13 04:24 start -> toolbox ... |
O Android ainda usa por padrão o SQLite como gerenciador de banco de dados para as aplicações e o OpenGl/ES como biblioteca gráfica, dentre outras bibliotecas disponíveis.
OS SERVIÇOS BÁSICOS
Se listarmos os processos rodando em um dispositivo com Android, a saída será mais ou menos essa:
# ps USER PID PPID VSIZE RSS WCHAN PC NAME root 1 0 504 416 c00c1b9c 0000877c S /init root 2 0 0 0 c0072c34 00000000 S kthreadd root 3 2 0 0 c00656f0 00000000 S ksoftirqd/0 root 4 2 0 0 c008a23c 00000000 S watchdog/0 root 5 2 0 0 c006fc14 00000000 S events/0 root 6 2 0 0 c006fc14 00000000 S khelper root 10 2 0 0 c0078bc8 00000000 S async/mgr root 13 2 0 0 c006fc14 00000000 S suspend root 229 2 0 0 c009ef8c 00000000 S sync_supers root 231 2 0 0 c009f7bc 00000000 S bdi-default root 233 2 0 0 c006fc14 00000000 S kblockd/0 root 246 2 0 0 c006fc14 00000000 S ksuspend_usbd root 250 2 0 0 c0240bc4 00000000 S khubd root 253 2 0 0 c0269074 00000000 S kseriod root 261 2 0 0 c006fc14 00000000 S twl4030-irqchip root 262 2 0 0 c0207a40 00000000 S twl4030-irq root 292 2 0 0 c006fc14 00000000 S kmmcd root 308 2 0 0 c006fc14 00000000 S musb_hdrc root 310 2 0 0 c006fc14 00000000 S rpciod/0 root 319 2 0 0 c008a434 00000000 S khungtaskd root 320 2 0 0 c009a628 00000000 S kswapd0 root 322 2 0 0 c006fc14 00000000 S aio/0 root 323 2 0 0 c006fc14 00000000 S nfsiod root 324 2 0 0 c006fc14 00000000 S crypto/0 root 474 2 0 0 c021a7f0 00000000 S mtdblockd root 546 2 0 0 c006fc14 00000000 S kondemand/0 root 547 2 0 0 c006fc14 00000000 S kconservative/0 root 553 2 0 0 c006fc14 00000000 S usbhid_resumer root 557 2 0 0 c02e760c 00000000 S binder_deferred root 589 2 0 0 c02d7f9c 00000000 S mmcqd root 609 1 488 172 c00c1b9c 0000877c S /sbin/ueventd root 887 1 736 332 c00633f0 afd0c63c S /system/bin/sh system 889 1 808 248 c02e69e8 afd0b81c S /system/bin/servicemanager root 890 1 3864 584 ffffffff afd0becc S /system/bin/vold root 891 1 3836 556 ffffffff afd0becc S /system/bin/netd root 892 1 668 256 c031bfb8 afd0c1ec S /system/bin/debuggerd root 893 1 1272 540 c0076e64 afd0becc S /system/bin/rild root 894 1 61364 26520 c00c1b9c afd0b964 S zygote media 895 1 26872 5132 ffffffff afd0b81c S /system/bin/mediaserver bluetooth 896 1 1256 564 c00c1b9c afd0c82c S /system/bin/dbus-daemon root 897 1 816 308 c0373d78 afd0b57c S /system/bin/installd keystore 898 1 1744 424 c031bfb8 afd0c1ec S /system/bin/keystore system 899 1 736 328 c00633f0 afd0c63c S /system/bin/sh root 901 1 3352 156 ffffffff 00008294 S /sbin/adbd root 948 2 0 0 c006fc14 00000000 S pvr_timer/0 root 962 2 0 0 c006fc14 00000000 S pvr_workqueue system 978 894 166280 70284 ffffffff afd0b81c S system_server root 988 2 0 0 c006fc14 00000000 S omaplfb app_21 1057 894 78324 23540 ffffffff afd0c7ac S com.android.inputmethod.latin radio 1066 894 85216 23928 ffffffff afd0c7ac S com.android.phone system 1067 894 76424 27816 ffffffff afd0c7ac S com.android.systemuiueventd app_14 1068 894 83528 33064 ffffffff afd0c7ac S com.android.launcher app_3 1120 894 76456 23620 ffffffff afd0c7ac S android.process.acore app_1 1180 894 73848 22596 ffffffff afd0c7ac S com.android.email app_4 1192 894 73320 22316 ffffffff afd0c7ac S android.process.media app_2 1215 894 71424 21416 ffffffff afd0c7ac S com.android.bluetooth app_10 1243 894 72164 21456 ffffffff afd0c7ac S com.android.deskclock app_25 1263 894 81444 21620 ffffffff afd0c7ac S com.android.mms app_26 1290 894 72016 21800 ffffffff afd0c7ac S com.android.providers.calendar app_16 1309 894 71832 21144 ffffffff afd0c7ac S com.android.quicksearchbox app_17 1323 894 71388 20588 ffffffff afd0c7ac S com.android.music app_27 1336 894 70796 20068 ffffffff afd0c7ac S com.android.protips app_13 1352 894 72452 21376 ffffffff afd0c7ac S com.cooliris.media root 5267 2 0 0 c00ced38 00000000 S flush-0:12 system 5336 899 884 272 c0076e64 afd0becc S sleep root 5348 887 896 312 00000000 afd0b57c R ps root 5349 1 740 332 c00633f0 afd0c63c S /system/bin/sh |
Três pontos interessantes aqui:
1. O processo “init” é o pai de todos os processos que rodam em user space, como o “/sbin/ueventd” e o “/system/bin/mediaserver“.
2. O processo “kthreadd” é o pai de todas as threads do kernel como o “ksoftirqd” e o “khelper“.
3. O processo “zygote” é o pai de todas as aplicações rodando no Android, como o “android.process.media” e o “com.android.email“. Tudo que roda em cima de uma máquina virtual, tem como antecessor comum o processo “zygote“.
A MÁQUINA VIRTUAL DALVIK
E por falar em máquina virtual, o Android usa uma implementação da JVM chamada Dalvik. A Dalvik não consome bytecode Java, mas sim dexcode. Para isso, o Google desenvolveu uma ferramenta, chamada “dx“, que converte Java bytecodes (*.class) em dexcodes (*.dex).
Além disso, desde a versão 2.2 (Froyo), o Android possui uma implementação de JIT (Just-in-time), que compila dexcodes para a arquitetura-alvo em tempo de execução, tornando a execução dos processos consideravelmente mais rápidas, já que não precisa ficar interpretando dexcodes.
Junto com a máquina virtual Dalvik, o Android usa o framework Apache Harmony, desenvolvido pela Apache Software Fundation como biblioteca padrão de classes Java.
AS APLICAÇÕES
As aplicações são escritas em Java. Existem bibliotecas Java disponíveis para acessar todos os recursos do dispositivo.
Ao executar uma aplicação, o processo “zygote” cria uma instância da máquina virtual Dalvik para executá-la. E cada aplicação roda com um UID (user ID) diferente. Dê uma olhada na coluna “USER” da listagem de processos acima. Isso protege as aplicações umas das outras, e do sistema como um todo, limitando acesso aos arquivos do sistema e aos recursos do dispositivo através de permissões de usuário.
No próximo artigo sobre o Android, iremos estudar o processo de boot e os principais serviços do sistema em mais detalhes.
Até lá!
Um abraço,
Sergio Prado