Introdução ao funcionamento interno do Android

- por Sergio Prado

Categorias: Android Tags: , ,

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

Navegue
Creative Commons Este trabalho de Sergio Prado é licenciado pelo
Creative Commons BY-NC-SA 3.0.