Você está na página 1de 333

ANDROID EMBARCADO

Sergio Prado
sergio.prado@e-labworks.com
https://www.linkedin.com/in/sprado
SOBRE ESTE DOCUMENTO
Slides do treinamento de Android embarcado v2.0.0.

A versão mais atual dos slides está disponível no site da Embedded Labworks.
https://e-labworks.com/training/aem/slides

Este documento é disponibilizado sob a licença Creative Commons Attribution-


ShareAlike 4.0 International (CC BY-SA 4.0). Os termos completos desta licença estão
disponíveis no link abaixo:
https://creativecommons.org/licenses/by-sa/4.0/
SOBRE O INSTRUTOR
Mais de 25 anos de experiência em desenvolvimento de software para sistemas
embarcados, atuando principalmente em projetos com Linux embarcado, Android
embarcado e sistemas operacionais de tempo real.

Sócio da Embedded Labworks, onde atua com consultoria, treinamento e


desenvolvimento de software para sistemas embarcados.
https://e-labworks.com

Ativo na comunidade de sistemas embarcados no Brasil, sendo um dos criadores do


site Embarcados e mantenedor de alguns blogs sobre assuntos da área.
https://sergioprado.blog

Colaborador de alguns projetos de software livre, incluindo o Buildroot, o Yocto


Project e o kernel Linux.
AGENDA DO TREINAMENTO
DIA 1: Introdução ao sistema operacional Android, código-fonte e AOSP, sistema de
build, arquitetura interna do sistema operacional, criação e customização de
produtos.

DIA 2: Camada nativa do Android, sistema de inicialização, compilação de módulos,


camada de abstração de hardware (HAL).

DIA 3: Framework e system services, desenvolvimento de serviços do framework,


desenvolvimento de APIs e aplicações, execução de aplicações dedicadas no
Android.
PRÉ-REQUISITOS
Usuário intermediário de distribuições GNU/Linux.

Terminal de comandos (ls, cp, cat, grep, find, vi, etc).

Ferramenta de controle de versão Git.

Arquitetura de sistemas com Linux embarcado (toolchain, bootloader, kernel, rootfs).

Conhecimentos intermediários nas linguagens C, C++ e Java.


AMBIENTE DE LABORATÓRIO
$ tree -L 2 /opt/labs
/opt/labs
├── dl
│ ├── AndroSensor.apk
│ ├── bootanimation.zip
│ ├── busybox.tar.bz2
│ ├── evtest.tar.bz2
│ └── patches.tar.bz2
├── docs
│ ├── agenda.html
│ ├── answers.html
│ ├── install.html
│ ├── labs.html
│ ├── slides.pdf
│ └── version.txt
├── ex
│ └── android
└── tools
├── emu
├── prepare-aosp.sh
├── prepare.cfg
└── prepare.sh
DURANTE O TREINAMENTO
Pergunte...

Expresse seu ponto de vista...

Troque experiências...

Ajude...

Participe!
INSTRUÇÕES PARA AULAS ONLINE
Não é permitido gravar o treinamento, mesmo para uso pessoal.

É recomendado ficar mutado durante as apresentações.

Para participar, utilize o chat ou o botão "Levantar a mão".


O uso da câmera é opcional.

Reporte qualquer problema com o áudio, vídeo ou compartilhamento de tela.


Se por acaso a conexão do instrutor cair, entre no link e espere-o voltar.

Interaja e participe!
ANDROID EMBARCADO

INTRODUÇÃO AO SISTEMA OPERACIONAL ANDROID


HISTÓRICO
2003: Começou como uma startup chamada Android Inc. em Palo Alto/CA, focada no
desenvolvimento de um sistema operacional aberto para smartphones.

2005: Android Inc. comprada pelo Google.

2007: Criada a Open Handset Alliance, um consórcio de empresas com interesse na


área de dispositivos móveis (Google, Intel, TI, Qualcomm, Nvidia, Motorola, HTC,
Samsung, etc).

2008: Sai a versão 1.0 do Android (HTC Dream).


VERSÕES
Desde então, uma ou duas novas versões do Android são lançadas todo ano:
Ano Versão Ano Versão
2010 2.3 (Gingerbread) 2016 7 (Nougat)
2011 3.0/3.1/3.2 (Honeycomb) 2017 8 (Oreo)
2011 4.0 (Ice Cream Sandwich) 2018 9 (Pie)
2012 4.1/4.2/4.3 (Jelly Bean) 2019 10 (Quince Tart) *
2013 4.4 (KitKat) 2020 11 (Red Velvet Cake)
2014 5 (Lollipop) 2021 12 (Snow Cone)
2015 6 (Marshmallow) 2022 13 (Tiramisu)

Este treinamento é baseado na versão 13.0.


ANDROID EM SISTEMAS EMBARCADOS
O sistema operacional Android ultrapassou a barreira dos dispositivos móveis e
atualmente é utilizado em diferentes soluções de sistemas embarcados:
Televisores inteligentes.

Centrais de entretenimento multimedia veicular.

Equipamentos de TV à cabo.

Máquinas de cartão de crédito.

Terminais de auto-atendimento.

Relógios inteligentes.

Equipamentos de linha branca (geladeiras, máquinas de lavar, etc).


PRINCIPAIS CARACTERÍSTICAS
A base do sistema operacional (AOSP) é de código aberto.

Interface gráfica com uma experiência bastante familiar.

Vasto ecossistema de aplicações (~ 2.7M de aplicações em maio/2023).


https://www.appbrain.com/stats/number-of-android-apps

Framework completo para desenvolvimento de aplicações, com APIs, IDE (Android


Studio), emulador, ferramentas de depuração e testes, documentação, livros, vídeos,
etc.
PRINCIPAIS CARACTERÍSTICAS (CONT.)
Linguagem base de programação (Java) é familiar e difundida.
Kotlin, a nova linguagem oficial do Android, é mais amigável e também bastante
difundida entre os desenvolvedores.

Suporte total a tecnologias Web.

Suporte a hardware "out-of-the-box", incluindo:


Aceleradores gráficos via OpenGL ES.

Tecnologias de comunicação sem fio (Bluetooth, WiFi, NFC, GSM, CDMA, UMTS,
LTE, etc).

Sensores (acelerômetro, giroscópio, compasso, etc).


ANDROID OPEN SOURCE PROJECT
O Android é basicamente baseado em dois grandes projetos:
Kernel Linux (modificado).

Plataforma Android (AOSP).

A cada versão, o Google libera o código-fonte do projeto através do Android Open


Source Project (AOSP).
https://source.android.com/

Apenas alguns dispositivos são suportados nativamente pelo AOSP, incluindo os


últimos dispositivos do Google (Pixel), emuladores e algumas placas de referência
(DragonBoard 845c, HiKey 960, Khadas VIM3, etc).
https://source.android.com/docs/setup/create/devices
COMUNIDADE E COLABORAÇÃO
Diversos grupos de discussão estão disponíveis para se comunicar com os
desenvolvedores do projeto:
https://groups.google.com/d/forum/android-platform

Qualquer pessoa pode contribuir com o projeto através da ferramenta de revisão de


código Gerrit.
https://source.android.com/docs/setup/contribute
https://android-review.googlesource.com

A evolução do projeto e as funcionalidades que estarão disponíveis em uma futura


versão do Android são controladas exclusivamente pelo Google.
https://code.google.com/p/android/issues/entry
LICENÇAS
A grande maioria dos componentes de software estão sob as licenças permissivas
Apache e BSD, dando liberdade aos fabricantes de decidirem se desejam liberar o
código-fonte alterado (licenças permissivas exigem apenas atribuição de autoria).

Alguns componentes de software estão sob a família de licenças GPL/LGPL (o Google


evita ao máximo licenças GPL).

Algumas aplicações do Google são proprietárias (Google Play, Gmail, Google Maps,
YouTube, etc).
Estas aplicações estão disponíveis em um pacote chamado Google Mobile
Services (GMS), e para obtê-las é necessário certificar o dispositivo (ACP).
CERTIFICAÇÃO
Para que o dispositivo possa ter a marca Android e utilizar as aplicações do Google, é
necessário certificá-lo através do Android Compatibility Program (ACP):
Compatibility Definition Document (CDD): descreve os requisitos necessários de
software e hardware para que um dispositivo possa ser considerado compatível
com o Android.

Compatibility Test Suite (CTS): ferramenta que fornece um conjunto de testes


unitários a serem executados no dispositivo para garantir sua compatibilidade
com o Android.

Mais informações sobre o processo de certificação no site do projeto:


https://source.android.com/compatibility/overview
VARIANTES DO ANDROID
Variantes oficiais do Android providas pelo Google:
Android Phone.

Android Tablet.

Android Automotive.

Android TV.

Android Wear.

Existem também versões alternativas do Android desenvolvidas pela comunidade,


incluindo LineageOS (antiga CyanogenMod), CopperheadOS, Replicant e
ProtonAOSP.
ARQUITETURA DE SISTEMAS GNU/LINUX
ARQUITETURA DO ANDROID
ANDROID EMBARCADO

CÓDIGO-FONTE
AOSP E REPOSITÓRIOS GIT
O AOSP é versionado pelo Google através do Git.

Porém, o projeto é dividido em centenas de repositórios Git.


Se o projeto fosse gerenciado por apenas um repositório Git seria lento para
baixar e difícil de gerenciar!

Os repositórios Git do Android podem ser acessados no link abaixo:


https://android.googlesource.com/
ANDROID.GOOGLESOURCE.COM
FERRAMENTA REPO
Para gerenciar as centenas de repositórios Git existentes no AOSP, o Google criou
uma ferramenta chamada repo.
https://android.googlesource.com/tools/repo

O repo pode ser instalado conforme instruções no link abaixo:


https://source.android.com/docs/setup/download#installing-repo

Através de um arquivo de manifesto que descreve todos os repositórios que


compõem o projeto (manifest.xml), a ferramenta repo é capaz de baixar e gerenciar
uma versão específica do AOSP.
MANIFEST.XML
<?xml version="1.0" encoding="UTF-8"?>
<manifest>

<remote name="aosp"
fetch=".."
review="https://android-review.googlesource.com/" />
<default revision="refs/tags/android-13.0.0_r44"
remote="aosp"
sync-j="4" />

<superproject name="platform/superproject" remote="aosp" revision="android-13.0.0_r44" />


<contactinfo bugurl="go/repo-bug" />

<project path="build/make" name="platform/build" groups="pdk" >


<project path="build/bazel" name="platform/build/bazel" groups="pdk" >
<project path="build/blueprint" name="platform/build/blueprint" groups="pdk,tradefed" />
<project path="build/pesto" name="platform/build/pesto" groups="pdk" />
<project path="build/soong" name="platform/build/soong" groups="pdk,tradefed" >
<project path="art" name="platform/art" groups="pdk" />
<project path="bionic" name="platform/bionic" groups="pdk" />
<project path="bootable/recovery" name="platform/bootable/recovery" groups="pdk" />
<project path="cts" name="platform/cts" groups="cts,pdk-cw-fs,pdk-fs" />
...
BAIXANDO O AOSP COM O REPO
$ repo init -u https://android.googlesource.com/platform/manifest

$ repo sync -j8

$ ls
Android.bp cts hardware prebuilts
art dalvik kernel sdk
bionic developers libcore system
bootable development libnativehelper test
bootstrap.bash device packages toolchain
build external pdk tools
BUILD frameworks platform_testing WORKSPACE
BAIXANDO UMA VERSÃO ESPECÍFICA
Por padrão, o repo irá baixar a branch de desenvolvimento (master) do AOSP.

Para baixar uma versão específica, basta indicar o branch ou tag com o parâmetro -b:
$ repo init -u https://android.googlesource.com/platform/manifest -b android-13.0.0_r44

A lista completa de branches e tags existentes está disponível no site do projeto:


https://source.android.com/setup/start/build-numbers
SUBCOMANDOS DO REPO
repo diff: faz um diff em todos os repositórios Git.

repo status: verifica o status de todos os repositórios Git.

repo start: cria um novo branch no AOSP.

repo branches: exibe os branches existentes.

repo forall: executa um comando em todos os repositórios Git.

repo help: exibe um menu completo de opções.


REPO E COLABORAÇÃO
O Google desenvolveu uma ferramenta chamada Gerrit para facilitar o processo de
colaboração e revisão de código.
https://android-review.googlesource.com

Através das ferramentas git e repo, qualquer pessoa pode contribuir com o
desenvolvimento do Android:

$ repo start <nome_do_branch>

$ git add -A
$ git commit -s

$ repo upload
FORKS DO AOSP
O AOSP é o repositório oficial do Android, mas oferece um suporte limitado a
dispositivos de hardware.

Fabricantes de kits de desenvolvimento podem manter arquivos de manifesto


específicos para suas plataformas de hardware:
https://github.com/technexion-android/manifest
https://github.com/hardkernel/android
https://github.com/raspberry-vanilla/android_local_manifest

A comunidade pode manter forks específicos do Android com foco em dispositivos


de consumo (smartphones e tablets):
https://github.com/LineageOS/android
https://github.com/ProtonAOSP/android_manifest
FORKS DO AOSP (CONT.)
Fabricantes de SoC (System-on-Chip) como a NXP e a Qualcomm também
disponibilizam para seus clientes uma versão do AOSP modificada para seus
processadores:
Android OS for i.MX Applications Processors
https://developer.qualcomm.com/hardware/dragonboard-410c/software

É possível também encontrar alguns fabricantes de hardware que disponibilizam a


árvore inteira do código-fonte do AOSP através de um arquivo compactado!
DIRETÓRIOS: HARDWARE
bootable/: imagem de referência da partição de recovery.

kernel/: arquivos de configuração, imagens pré-compiladas e scripts de teste do


kernel.

hardware/: definição das interfaces das HALs e implementações de referência para


alguns dispositivos de hardware.

device/: configurações de produto.


DIRETÓRIOS: CAMADA NATIVA
bionic/: biblioteca C padrão do Android.

libnativehelper/: biblioteca para interface JNI.

system/: aplicações e bibliotecas da camada nativa.

external/: projetos externos utilizados pelo Android (libusb, sqlite, mksh, etc).
DIRETÓRIOS: FRAMEWORK E APLICAÇÕES
dalvik/: máquina virtual Dalvik.

art/: máquina virtual ART.

libcore/: biblioteca Java padrão (fork do projeto Apache Harmony).

frameworks/: código-fonte do framework (APIs e serviços).

packages/: aplicações Android.


DIRETÓRIOS: FERRAMENTAS
sdk/: ferrramentas do SDK (Software Development Kit).

pdk/: ferramentas do PDK (Platform Development Kit).

development/, test/, platform_testing/, developers/, tools/: ferramentas de


desenvolvimento, testes e depuração.

cts/: ferramentas do CTS (Compatibility Test Suite).


DIRETÓRIOS: BUILD SYSTEM
build/: scripts e arquivos de compilação do Android.

prebuilts/: binários pré-compilados, incluindo o toolchain (baseado no Clang).

toolchain/: scripts de teste e benchmark do toolchain.


NAVEGANDO NO CÓDIGO-FONTE
Como não existe muita documentação a respeito do AOSP, é comum a necessidade
de navegar no código-fonte para entender uma determinada funcionalidade do
Android.

Para isso, podemos gerar um arquivo de projeto do AOSP e utilizar o Android Studio
para navegar no código-fonte do Android.
$ cat development/tools/idegen/README
$ cat tools/asuite/aidegen/README.md

Outras formas de navegar no código-fonte oficial do Android incluem o Code Search


do Google e o AOSPXRef.
https://cs.android.com
https://aosp.opersys.com
http://aospxref.com
ANDROID CODE SEARCH
AOSP CROSS REFERENCE
AOSP CROSS REFERENCE (CONT.)
LABORATÓRIO 1

CÓDIGO-FONTE DO ANDROID
ANDROID EMBARCADO

SISTEMA DE BUILD
SISTEMAS DE BUILD
Sistemas de build (build systems) possuem o objetivo principal de integrar todos os
componentes de software de um sistema Linux (toolchain, bootloader, kernel,
bibliotecas e aplicações) e construir a imagem do sistema operacional.

No Linux, os dois principais projetos de sistemas de build são:


Buildroot.

Yocto Project/OpenEmbedded.

No entanto, o Android possui seu próprio sistema de build!


SISTEMA DE BUILD DO AOSP
Até o Android 6 (Marshmallow), o sistema de build do Android era puramente
baseado em makefiles, processados pela ferramenta make.
As instruções de compilação de todos os componentes de software eram
definidas em arquivos Android.mk.

Este build system tinha diversas deficiências, incluindo instabilidade, baixa


performance e dificuldade para rastrear e depurar sua execução.

Foi substituído no Android 7 (Nougat) pelo sistema de build Soong.


https://source.android.com/setup/build#what_is_soong/
https://android.googlesource.com/platform/build/soong
SISTEMA DE BUILD SOONG
No sistema de build Soong, as regras para compilar os componentes de software são
definidas em arquivos de Blueprint (Android.bp), que possuem uma sintaxe parecida
com JSON.

Os arquivos de Blueprint são processados e transformados em arquivos com


extensão .ninja, que possuem todas as regras para processar os componentes de
software do sistema operacional.

Os arquivos com extensão .ninja são processados pela ferramenta Ninja, responsável
por executar os comandos de compilação, gerar os artefatos de software e construir
as imagens do sistema operacional.
https://ninja-build.org/
MIGRAÇÃO PARA O SOONG
Até o Android 13, nem todos os arquivos de make (Android.mk) foram convertidos em
arquivos de Blueprint (Android.bp).
$ find . -name Android.bp | wc -l
8857
$ find . -name Android.mk | wc -l
967

Por este motivo, o Google criou uma ferramenta chamada kati para converter os
arquivos Android.mk em arquivos .ninja.
https://github.com/google/kati

Aos poucos, os arquivos Android.mk estão sendo convertidos em Android.bp, o que irá
eliminar a necessidade de makefiles e da ferramenta kati no AOSP.
DIAGRAMA: SOONG
BAZEL
Bazel é um sistema de build de código-aberto mantido pelo Google e utilizado
amplamento em seus projetos de software.
https://bazel.build

Existe uma iniciativa de longo prazo para converter o buildsystem do AOSP para
Bazel.
Os makefiles de produtos são convertidos para o formato Starlark (*.blz).

Os arquivos de Blueprint são convertidos para arquivos de build do Bazel


(BUILD).

Soong e ninja são substituídos pelo Bazel para a execução do processo de build.

Veja uma apresentação do time do Android para mais detalhes:


https://www.youtube.com/watch?v=VyW04BwiBSs
PRÉ-REQUISITOS PARA COMPILAR O AOSP
É recomendada uma máquina de 64 bits com o Ubuntu (configuração usada e
testada pelo Google), mas o sistema de build deve funcionar em outras distribuições
Linux.

É necessária uma máquina com boa capacidade de processamento (8+ CPUs),


bastante memória (16GB+ RAM) e espaço em disco (256GB+ para uma compilação
completa, SSD recomendado).
https://source.android.com/docs/setup/start/requirements#hardware-requirements

Diversos pré-requisitos de software: git, python 3, make, unzip, curl, libncurses, etc.

Mais informações sobre a configuração do ambiente de compilação:


https://source.android.com/setup/build/initializing
INICIALIZANDO O AMBIENTE DE BUILD
O primeiro passo para utilizar o sistema de build do Android é carregar o script de
configuração do ambiente (build/envsetup.sh):
$ source build/envsetup.sh

Este script irá alterar a configuração do terminal atual, definindo alguns comandos,
variáveis de ambiente e macros que serão utilizadas durante o processo de
compilação.

O comando source é necessário para garantir que as alterações sejam aplicadas no


terminal corrente do usuário.
ENVSETUP.SH
Estes são alguns dos comandos criados pelo envsetup.sh:
lunch: seleciona o produto e a variante de build para compilação.

godir: vai para o diretório contendo o arquivo especificado.

gomod: vai para o diretório de um módulo.

croot: volta para o diretório raiz do AOSP (ou subdiretório a partir da raiz).

cgrep: executa uma busca em arquivos .c, .cpp e .h.

jgrep: executa uma busca em arquivos .java.

hmm: exibe a lista completa de comandos disponíveis.


PRODUTO E VARIANTE
Após executar o script envsetup.sh, o próximo passo é configurar o produto e a
variante de build que deseja-se compilar.

Um produto define as características de hardware e software que serão utilizadas


para gerar a imagem do sistema operacional.

A variante de build indica o tipo de compilação que deseja-se fazer:


user: build de produção.

userdebug: build de produção com acesso root e ferramentas de depuração.

eng: build de desenvolvimento com acesso root e diversas ferramentas de teste,


desenvolvimento e depuração.
CONFIGURANDO O PRODUTO
A configuração do produto e da variante de build é realizada através de variáveis de
ambiente, incluindo:
TARGET_PRODUCT: nome do produto.

TARGET_BUILD_VARIANT: variante de build.

Estas variáveis podem ser definidas manualmente ou em um arquivo chamado


buildspec.mk, que deve ser armazenado no diretório raiz do AOSP (ver exemplo em
build/buildspec.mk.default).

De qualquer forma, o mecanismo mais comum de configuração do produto e da


variante de build é através do comando lunch.
LUNCH
$ lunch

You're building on Linux

Lunch menu .. Here are the common combinations:


1. aosp_arm-eng
2. aosp_arm64-eng
3. aosp_barbet-userdebug
4. aosp_bluejay-userdebug
5. aosp_bluejay_car-userdebug
6. aosp_bramble-userdebug
7. aosp_bramble_car-userdebug
8. aosp_car_arm-userdebug
9. aosp_car_arm64-userdebug
10. aosp_car_x86-userdebug
11. aosp_car_x86_64-userdebug
12. aosp_cf_arm64_auto-userdebug
13. aosp_cf_arm64_phone-userdebug
14. aosp_cf_x86_64_foldable-userdebug
...

Which would you like? [aosp_arm-eng]


Pick from common choices above (e.g. 13) or specify your own (e.g. aosp_barbet-eng):
EXECUTANDO O LUNCH
O comando lunch irá exibir uma lista de opções no formato <produto>-<variante>
(também chamado de combo), e o usuário deverá selecionar uma destas opções.

É possível também executar o comando lunch passando diretamente o combo:


$ lunch aosp_cf_x86_phone-eng

Ao selecionar o combo, o comando lunch irá criar as variáveis de ambiente


necessárias para a compilação:
$ env | grep "^ANDROID\|^TARGET"
ANDROID_BUILD_TOP=/opt/build/android
TARGET_PRODUCT=aosp_cf_x86_phone
TARGET_BUILD_VARIANT=eng
...
COMPILANDO
Após selecionar o produto, o Android pode ser compilado com o comando m
(funciona dentro de qualquer diretório do AOSP):
$ m

O parâmetro -j pode ser utilizado para paralelizar a compilação de acordo com a


quantidade de CPUs da máquina de desenvolvimento.
$ m -j4

Se passado apenas o -j, o sistema de build irá automaticamente selecionar a


quantidade de threads de compilação.
$ m -j
SAÍDA DA COMPILAÇÃO
Todo o processo de compilação do Android acontece dentro do diretório out/:
No diretório out/host/ são instaladas as ferramentas, executáveis e bibliotecas
compiladas para o host.

No diretório out/target/ são instalados os artefatos de software gerados para o


target.

As imagens finais estarão disponíveis no diretório de saída do produto em


out/target/product/<nome_do_produto>/.
IMAGENS FINAIS
$ ls out/target/product/vsoc_x86/*.img
out/target/product/vsoc_x86/boot.img out/target/product/vsoc_x86/system_other.img
out/target/product/vsoc_x86/dtb.img out/target/product/vsoc_x86/userdata.img
out/target/product/vsoc_x86/init_boot.img out/target/product/vsoc_x86/vbmeta.img
out/target/product/vsoc_x86/metadata.img out/target/product/vsoc_x86/vbmeta_system.img
out/target/product/vsoc_x86/misc.img out/target/product/vsoc_x86/vendor-bootconfig.img
out/target/product/vsoc_x86/odm_dlkm.img out/target/product/vsoc_x86/vendor_boot-debug.img
out/target/product/vsoc_x86/odm.img out/target/product/vsoc_x86/vendor_boot.img
out/target/product/vsoc_x86/product.img out/target/product/vsoc_x86/vendor_boot-test-harness.img
out/target/product/vsoc_x86/ramdisk.img out/target/product/vsoc_x86/vendor_dlkm.img
out/target/product/vsoc_x86/super_empty.img out/target/product/vsoc_x86/vendor.img
out/target/product/vsoc_x86/super.img out/target/product/vsoc_x86/vendor_ramdisk-debug.img
out/target/product/vsoc_x86/system_dlkm.img out/target/product/vsoc_x86/vendor_ramdisk.img
out/target/product/vsoc_x86/system_ext.img out/target/product/vsoc_x86/vendor_ramdisk-test-harness
out/target/product/vsoc_x86/system.img
TARGETS DE COMPILAÇÃO
O comando m possui diversos targets de compilação que podem ser exibidos com a
opção help:
$ m help

Alguns targets interessantes:


droid: target padrão (quando nenhum target é passado).

clean: remove o diretório de saída da compilação (rm -rf out/).

snod: regera a imagem da partição system.

vnod: regera a imagem da partição vendor.

pnod: regera a imagem da partição product.


COMPILANDO MÓDULOS ESPECÍFICOS
O comando m é capaz de compilar módulos específicos do sistema de build:
$ m logcat

Módulos dentro de um diretório específico do sistema de build podem ser


compilados com o comando mm:
$ cd system/logging/logcat
$ mm logcat

O mesmo pode ser feito com o comando mmm, passando o diretório do código-fonte
do módulo que deseja-se compilar:
$ mmm system/logging/logcat:logcat
REGERANDO IMAGENS
Caso tenha alterado uma aplicação ou as configurações de um produto, basta
utilizar o comando m para recompilar e regerar as imagens do Android:
$ m -j

De forma alternativa, é possível recompilar apenas o módulo alterado e regerar a


imagem correspondente (em alguns casos, este método pode ser mais rápido):
$ m logcat
$ m snod
TESTE RÁPIDO DE ALTERAÇÕES
Caso tenha alterado o código de uma aplicação e queira fazer um teste rápido, ao
invés de recompilar o Android e regerar as imagens, é possível recompilar apenas a
aplicação e transferí-la para o dispositivo com o ADB:
$ m logcat
$ adb remount
$ adb sync

O comando 'adb remount' pode ser substituído pelo syswrite.

Este procedimento é muito mais rápido comparado a recompilar e regerar as


imagens do Android, porém pode não funcionar em algumas situações como
alterações nos arquivos de inicialização do sistema ou na configuração do produto.
RECURSOS ADICIONAIS
Documentação básica do comando m disponível no código-fonte do AOSP em
build/make/Usage.txt.

Após um build, informações mais completas do processo de build estarão


disponíveis em um arquivo de log e podem ser extraídas com o comando abaixo:
$ gzip -cd out/verbose.log.gz | less -R

O sistema de build do Android está em constante mudanças, e o documento abaixo é


bastante útil para acompanhar estas mudanças:
https://android.googlesource.com/platform/build/+/master/Changes.md

Ambiente de integração contínua do AOSP:


https://ci.android.com/builds/branches/aosp-master/grid
EMULANDO O ANDROID
A imagem do sistema operacional Android podem ser emulada em uma máquina
virtual chamada Android Virtual Device (AVD).
Existem duas implementações: Android Emulator (Goldfish) e Cuttlefish.

O Android Emulator (Goldfish) é a implementação mais antiga, fornecendo uma


interface através de uma aplicação desktop para interagir com o dispositivo.
Pode ser compilado via produtos sdk_phone_x86 e sdk_phone_x86_64.

O Cuttlefish é a implementação mais nova e fiel ao comportamento do AOSP,


provendo uma interface de interação com o dispositivo via WebRTC.
Pode ser compilado via produtos aosp_cf_x86_phone e aosp_cf_x86_64_phone.
COMPILANDO O ANDROID EMULATOR
Compilando uma imagem do Android Emulator:
$ source build/envsetup.sh
$ lunch sdk_phone_x86-eng
$ m -j

Executando o emulador:
$ emulator

Mais informações sobre o Android Emulator:


https://source.android.com/docs/setup/create/avd
ANDROID EMULATOR
COMPILANDO O CUTTLEFISH
Compilando uma imagem do Cuttlefish:
$ source build/envsetup.sh
$ lunch aosp_cf_x86_phone-eng
$ m -j

Executando o emulador:
$ launch_cvd

Mais informações sobre o Cuttlefish:


https://source.android.com/docs/setup/create/cuttlefish
https://android.googlesource.com/device/google/cuttlefish/
CUTTLEFISH
LABORATÓRIO 2

SISTEMA DE BUILD DO ANDROID


ANDROID EMBARCADO

ARQUITETURA DO SISTEMA OPERACIONAL


ARQUITETURA ANDROID
HARDWARE
HARDWARE TÍPICO COM ANDROID
CPU
Oficialmente, o Android suporta as arquiteturas ARM e x86 (32 bits e 64 bits).

No passado, o Android chegou a suportar outras arquiteturas de CPU, incluindo


MIPS.

Existe uma iniciativa para suportar o Android em RISC-V:


https://www.youtube.com/watch?v=70O_RmTWP58
https://ci.android.com/builds/branches/aosp-master/grid

Atualmente, é mais comum é encontrar o Android em dispositivos de arquitetura


ARM 64 bits (multicore, 1GHz+).
MEMÓRIA E ARMAZENAMENTO
Os requisitos de memória RAM e armazenamento do Android podem variar bastante,
dependendo do sistema a ser desenvolvido.

Requisitos típicos de RAM:


Sistema embarcado: mínimo 1G, recomendado 2G+.

Smartphone/tablet: mínimo 2G, recomendado 4G+.

Requisitos típicos de armazenamento (eMMC):


Sistema embarcado: 4G+.

Smartphone/tablet: 16G+.
OUTRAS CARACTERÍSTICAS COMUNS
Display com touchscreen.

Botões de navegação (Power, Volume, Home, Back, etc).


Os botões também podem ser emulados em software, ou o sistema pode utilizar
navegação por gestos.

Sensores (acelerômetro, magnetômetro, GPS, giroscópio, etc).

Interfaces de comunicação wireless (Bluetooth, WiFi, NFC, etc).

O CDD define requisitos mínimos de hardware por tipo de dispositivo (handheld,


watch, television, automotive, etc).
https://source.android.com/compatibility/android-cdd
BOOTLOADER
O BOOTLOADER
O bootloader é o código responsável por:
Inicializar o hardware.

Carregar e executar o sistema operacional.

Além destas funcionalidades básicas, a maioria dos bootloaders provê um terminal


de comandos para a execução de diferentes operações, incluindo:
Manipulação e configuração do processo de boot.

Formatação da flash e gravação de imagens.

Rotinas de diagnóstico do hardware.


BOOTLOADER NO ANDROID
Não é necessário nenhum recurso específico no bootloader para fazer o boot de um
sistema Android.
No entanto, o processo de certificação de um dispositivo Android pode exigir a
existência de alguns recursos no bootloader:
https://source.android.com/docs/core/architecture/bootloader

Como o bootloader é totalmente dependente do hardware, não existe o código-fonte


de bootloaders no AOSP.

Normalmente, fabricantes de SoC disponibilizam um bootloader de referência para


suas plataformas de hardware:
https://github.com/littlekernel/lk/wiki/Introduction
https://www.denx.de/wiki/U-Boot
FASTBOOT
Uma funcionalidade normalmente disponível em bootloaders de dispositivos com
Android é o fastboot:
system/core/fastboot/README.md

O fastboot é um protocolo de comunicação com o bootloader via USB ou TCP/IP,


acessível através de uma ferramenta de linha de comando chamada fastboot.

Possui diversas funcionalidades, incluindo configurar o dispositivo e atualizar as


imagens do sistema operacional.

A partir do Android 10, parte das funcionalidades do fastboot foram movidas para o
espaço de usuário do Android.
https://source.android.com/docs/core/architecture/bootloader/fastbootd
FERRAMENTA FASTBOOT
$ fastboot -h
usage: fastboot [OPTION...] COMMAND...

flashing:
update ZIP Flash all partitions from an update.zip package.
flashall Flash all partitions from $ANDROID_PRODUCT_OUT.
On A/B devices, flashed slot is set as active.
Secondary images may be flashed to inactive slot.
flash PARTITION [FILENAME] Flash given partition, using the image from
$ANDROID_PRODUCT_OUT if no filename is given.

basics:
devices [-l] List devices in bootloader (-l: with device paths).
getvar NAME Display given bootloader variable.
reboot [bootloader] Reboot device.

locking/unlocking:
flashing lock|unlock Lock/unlock partitions for flashing
flashing lock_critical|unlock_critical
Lock/unlock 'critical' bootloader partitions.
flashing get_unlock_ability
Check whether unlocking is allowed (1) or not(0).
...
LINUX KERNEL
VISÃO GERAL DO KERNEL
O KERNEL LINUX NO ANDROID
Assim como fazem boa parte das distribuições GNU/Linux, o kernel utilizado no
Android é alterado para suprir as necessidades do projeto.
Estas alterações são às vezes chamadas de Androidisms.

As mudanças no kernel são tão significativas que a camada de usuário do Android só


funciona com estas alterações!

Boa parte das alterações já estão integradas à versão oficial do kernel, sendo
possível executar um sistema Android em um kernel mainline.
Ainda é necessário adaptar a configuração para habilitar alguns recursos
necessários para executar o Android (Binder, Ashmem, etc).
BINDER
Binder é o principal mecanismo de IPC e RPC do Android, adicionando ao kernel a
capacidade de comunicação interprocessos e invocação remota de métodos.

No Android, toda a comunicação entre as aplicações, serviços, alguns daemons e


HALs acontece via Binder.

A interface do Binder é exposta para o usuário em arquivos no /dev, acessados


através de chamadas ioctl().
# ls /dev/*binder
/dev/binder /dev/hwbinder /dev/vndbinder
ASHMEM
Ashmem (Anonymous Shared Memory) é um mecanismo de compartilhamento de
memória entre processos utilizado no Android.

Algumas deficiências no mecanismo de compartilhamento de memória padrão do


Linux (POSIX SHM), como a possibilidade de vazamento de recursos, levaram a
criação desta nova interface pelo Google.
Por exemplo, esta implementação usa um contador de referência para destruir
regiões de memória que não estão sendo mais utilizadas por nenhum processo.

Através do arquivo /dev/ashmem, uma aplicação requisita uma região de memória e


compartilha com outros processos via Binder.
WAKELOCKS
O wakelock é um recurso de gerenciamento de energia do Android.

A idéia é colocar a CPU em modo de baixo consumo (sleep mode) sempre que
possível:
Quando uma aplicação quiser manter a CPU ligada, deve segurar um wakelock.

Quando não tiver ninguém segurando um wakelock, o kernel coloca a CPU em


modo de baixo consumo.

O controle é feito através de arquivos exportados pelo kernel:


# ls /sys/power/wake_*
/sys/power/wake_lock /sys/power/wake_unlock
LOW MEMORY KILLER
Quando o sistema fica sem memória, o kernel Linux executa por padrão o OOM (Out-
of-Memory) killer, que tenta matar alguns processos para liberar memória.

O OOM Killer não é previsível e pode matar um processo importante do sistema


operacional, e por este motivo, o Google desenvolveu o Low Memory Killer (LMK).

O LMK é acionado antes do OOM killer, complementando seu funcionamento:


Leva em consideração a prioridade e a ociosidade do processo.

Possibilita notificar um processo antes de encerrá-lo.

Em versões recentes do Android, boa parte da lógica do Low Memory Killer foi
movida para um daemon em espaço de usuário chamado lmkd.
DE ONDE VEM O CÓDIGO DO KERNEL?
Não existe código-fonte do kernel Linux no AOSP!

Mas o Google fornece uma versão base do kernel para dispositivos Android em um
projeto chamado Android Common Kernel (ACK).
https://source.android.com/devices/architecture/kernel/android-common
https://android.googlesource.com/kernel/common/

Se o fabricante de uma determinada plataforma de hardware (vendor) quiser


suportar o Android, irá utilizar o ACK como base para portar o kernel e fornecer o BSP
para seus usuários (OEM).
FORKS DO KERNEL ANDROID
GENERIC KERNEL IMAGE (GKI)
A quantidade excessiva de forks causa um grande problema de fragmentação, que
dificulta o processo de atualização do kernel em dispositivos Android em produção.

Para resolver este problema, o Google criou o projeto Generic Kernel Image (GKI).
https://source.android.com/devices/architecture/kernel/generic-kernel-image

A idéia é que todos os fabricantes de hardware utilizem o kernel padrão provido pelo
Google (ACK), e extendam o kernel através de módulos carregáveis, utilizando uma
interface definida e padronizada (KMI - Kernel Module Interface).

Na prática, você deve se preocupar com isso apenas se estiver portando o kernel
para rodar o Android em seu dispositivo, com o objetivo de certificá-lo.
ARQUITETURA GKI
ESPAÇO DE USUÁRIO
ESPAÇO DE USUÁRIO NO ANDROID
De forma geral, o espaço de usuário (user space) em um sistema Linux contém as
bibliotecas e aplicações que compõem o sistema operacional.
No Android, também é chamado de plataforma Android.

O espaço de usuário no Android é composto por três principais camadas:


Camada nativa: código nativo (C, C++, Rust) que roda fora da máquina virtual.

Framework: serviços e APIs que rodam dentro da máquina virtual.

Aplicações: applicações Android.

Estudaremos estas camadas em detalhes no decorrer do treinamento.


ORGANIZAÇÃO DOS ARQUIVOS
Em sistemas Linux, os componentes da camada de usuário são organizados de
acordo com os padrões FHS (Filesystem Hierarcy Standard) e LSB (Linux Standard
Base).
http://www.pathname.com/fhs/
https://refspecs.linuxfoundation.org/lsb.shtml

A maioria dos sistemas Linux estão de acordo com estes padrões:


Possibilita a interoperabilidade das aplicações em diferentes sistemas Linux.

Facilita a adaptação dos usuários ao migrar para outras distribuições Linux.

Mas o Android foge à regra!


ORGANIZAÇÃO DOS ARQUIVOS NO ANDROID
Onde estão diretórios importantes como o /lib e o /usr?
# ls /
acct debug_ramdisk odm sys
apex dev odm_dlkm system
bin etc oem system_dlkm
bugreports init postinstall system_ext
cache init.environ.rc proc vendor
config linkerconfig product vendor_dlkm
d lost+found sdcard
data metadata second_stage_resources
data_mirror mnt storage

Eles não existem!

Para entender como funciona a organização dos arquivos no Android, é necessário


estudar as diferentes partições que compõem seu sistema de arquivos.
PARTIÇÕES
Com o principal objetivo de facilitar a manutenção e o processo de atualização, o
sistema de arquivos do Android é bem modular e dividido em diferentes partições.
Código e dados são separados em diferentes partições.

Código é separado em partições por "origem" (Google, vendor, OEM, operadora).

Código é quebrado em diferentes partições para facilitar a atualização (boot,


módulos do kernel, radio, etc).

Mecanismo de update A/B (seamless system updates) duplica as partições.

Documentação detalhada sobre as principais partições no site do projeto.


https://source.android.com/devices/bootloader/partitions
AS PARTIÇÕES NO ANDROID 13
Partição Descrição
boot imagem do kernel (GKI)
init_boot imagem genérica do ramdisk
vendor_boot extensão da imagem de ramdisk provida pelo fabricante
system imagem base do sistema operacional (AOSP)
system_ext extensão da partição system com componentes do fabricante
vendor artefatos de software do fabricante do SoC (Qualcomm, Mediatek,
etc)
vendor_dlkm módulos do kernel do fabricante do SoC
odm artefatos de software do integrador (Motorola, Samsung, etc)
odm_dlkm módulos do kernel do integrador
AS PARTIÇÕES NO ANDROID 13 (CONT.)
Partição Descrição
product artefatos de software do produto, utilizado por exemplo para diferenciar
builds de operadora
userdata arquivos de configuração, aplicações instaladas e outros dados gerados
em tempo de execução
metadata informações utilizadas no processo de encriptação das partições
recovery imagem de recuperação utilizada no processo de atualização (OTA)
misc partição utilizada exclusivamente pela imagem de recovery
cache dados temporários como downloads ou imagens de update
radio software específico do rádio (caso o dispositivo possua este periférico)
IMAGENS PRINCIPAIS DO SISTEMA
PARTIÇÕES E PONTOS DE MONTAGEM
CONTEÚDO DAS PARTIÇÕES
# ls system
apex bin etc framework lib64 product usr xbin
app build.prop fonts lib priv-app system_ext vendor

# ls system_ext/
apex bin etc framework lib64 lost+found priv-app

# ls /vendor
apex build.prop framework lib64 odm pci.ids
bin etc lib lost+found overlay priv-app

# ls /odm
etc lib lost+found

# ls /product
app bin etc lib lib64 lost+found media overlay priv-app
PRINCIPAIS DIRETÓRIOS
Diretório Descrição
bin e xbin binários nativos
lib[64] bibliotecas nativas
etc arquivos de configuração da camada nativa
framework código do framework
usr arquivos de configuração do framework
app e priv-app aplicações Android (.apk)
apex artefatos de software no formato APEX
media arquivos de mídia (animação do boot, áudio, etc)
fonts fontes do sistema (.otf, .ttf, etc)
ORGANIZAÇÃO DA PARTIÇÃO USERDATA
# ls /data
adb cache misc_de ss
anr dalvik-cache nativetest system
apex data nativetest64 system_ce
app drm nfc system_de
app-asec fonts ota tombstones
app-ephemeral gsi ota_package unencrypted
app-lib gsi_persistent_data per_boot user
app-private incremental preloads user_de
app-staging local property vendor
backup lost+found resource-cache vendor_ce
benchmarktest media rollback vendor_de
benchmarktest64 mediadrm rollback-history
bootanim misc rollback-observer
bootchart misc_ce server_configurable_flags
GENERIC SYSTEM IMAGE (GSI)
GSI (Generic System Image) é uma imagem genérica da partição system que todo
dispositivo Android versão >= 8.1 deve ser capaz de executar.
https://source.android.com/setup/build/gsi

O principal uso desta imagem é para executar os testes de certificação (CTS e VTS).
A imagem de um sistema Android é substituída pela GSI e testada com o CTS e o
VTS para verificar se as interfaces implementadas pelo fabricante são
compatíveis com a última versão do Android.

A GSI pode ser baixada do ambiente de integração contínua do Android:


https://ci.android.com/builds/branches/aosp-android13-gsi/grid
SEGURANÇA NO ANDROID
O design do Android tem um foco grande na segurança do sistema operacional:
https://source.android.com/docs/security

Dentre os principais recursos de segurança do sistema operacional, temos:


Apenas o kernel e um pequeno conjunto de programas rodam com acesso root.

Uso do SELinux para controle de acesso aos recursos do sistema operacional.

Verificação da integridade e autenticidade das imagens (verified boot).

Encriptação da partição de dados.

Integração com um ambiente de execução seguro (TEE) e a disponibilidade de


uma infraestrutura de virtualização (AVF).
ATUALIZAÇÃO REMOTA
O Android possui um mecanismo integrado de atualização remota (OTA updates):
https://source.android.com/docs/core/ota

Alguns dos principais recursos providos pelo mecanismo de atualização do Android:


Segurança no transporte e verificação de assinatura das imagens de update.

Mecanismo de update do tipo A/B (seamless system updates).

Criação dinâmica de partições (dynamic partitions).

Atualização de componentes individuais (via Google Play) através dos containers


de arquivos APK (Android Package) e APEX (Android Pony EXpress).
ADB
Normalmente, o processo de depuração em sistemas embarcados acontece via porta
serial ou interface JTAG (para depuração em baixo nível).

Porém, dispositivos móveis e de consumo como celulares e tablets não possuem


normalmente estas interfaces.
O mais comum é ter apenas uma porta USB disponível.

É por este motivo que o Google desenvolveu o ADB (Android Debug Bridge).
https://developer.android.com/tools/adb
COMPONENTES DO ADB
O ADB é uma ferramenta de depuração que pode funcionar via USB ou TCP/IP.

É composto por quatro principais componentes:


Cliente ADB: roda no host e serve de interface (modo texto) para a execução de
comandos no target.

Servidor ADB: roda no host e gerencia a comunicação dos clientes ADB com o
daemon ADB no target.

Android USB Gadget: apenas no caso de conexões USB, é um driver no kernel que
recebe as mensagens enviadas pelo cliente ADB e repassa para o daamon ADB.

Daemon ADB: roda no target, recebe e trata os comandos recebidos do cliente


ADB no host.
ARQUITETURA DO ADB
ALGUNS COMANDOS ÚTEIS
Comando Descrição
devices lista os dispositivos disponíveis
logcat exibe os logs do sistema operacional
shell executa um comando ou inicia uma seção de terminal
root habilita acesso root (para builds do tipo userdebug)
push e pull transfere arquivos entre o host e o target
install e uninstall gerencia a instalação de aplicações Android (APK)
remount remonta as partições com permissão de escrita
sync sincroniza os arquivo de build com o target
help exibe todas as opções disponíveis
LABORATÓRIO 3

AMBIENTE DE EXECUÇÃO DO ANDROID


ANDROID EMBARCADO

PRODUTOS
O QUE É UM PRODUTO?
Um produto define todas as informações necessárias para a construção de uma
imagem do Android (arquitetura do hardware, periféricos suportados, conjunto de
aplicações, configurações específicas do dispositivo, etc).

Por padrão, o AOSP já vem com alguns produtos definidos, incluindo os últimos
dispositivos do Google (Pixel), algumas placas de referência (DragonBoard, HiKey,
etc) e emuladores para diversas arquiteturas de hardware.

Como as customizações do Android são feitas no produto, a criação de um produto é


uma das primeiras tarefas para portar o Android para uma plataforma de hardware.
CONFIGURAÇÃO DO PRODUTO
Até o momento (Android 13), a configuração do produto é feita em Makefiles (*.mk),
através da definição de diversas variáveis que são utilizadas para parametrizar o
processo de build do Android.
Na migração para o futuro build system do Android (Bazel), os Makefiles serão
substituídos por arquivos Starlark.
https://source.android.com/docs/setup/build/bazel/product_config/introduction

No AOSP, os arquivos de customização de um produto são armazenados em um


diretório no formato device/<company>/<product>.

O objetivo é centralizar no diretório do produto todos os arquivos de definição e


customização da imagem, facilitando a manutenção e o processo de atualização do
sistema operacional.
SUNFISH (PIXEL 4A)
$ ls device/google/sunfish
Android.bp init.power.rc
Android.mk init.qcom.modem_links.sh
AndroidProducts.mk init.qcom.usb.rc
aosp_sunfish_hwasan.mk init.qcom.usb.sh
aosp_sunfish.mk init.qti.getbootdevice.sh
audio init.qti.qseecomd.sh
audio_policy_configuration_a2dp_offload_disabled.xml init.radio.sh
audio_policy_configuration_bluetooth_legacy_hal.xml init.ramoops.sh
audio_policy_configuration.xml init.recovery.device.rc
audio_policy_volumes.xml init.sensors.sh
bluetooth_hearing_aid_audio_policy_configuration.xml json-c
bluetooth_power_limits_sunfish.csv keymaster
BoardConfig-common.mk manifest.xml
board-info.txt media_codecs_omx.xml
bootctrl media_codecs_performance_c2.xml
CleanSpec.mk media_codecs.xml
compatibility_matrix.xml media_profiles_V1_0.xml
component-overrides.xml media_profiles.xml
config.fs METADATA
default-permissions.xml nfc
...
CRIANDO UM PRODUTO
Para criar um produto, o primeiro passo é criar um diretório no formato
device/<company>/<product> ou vendor/<company>/<product>:
$ mkdir -p vendor/labworks/superdroid/

Dentro deste diretório, é necessário criar (no mínimo) três makefiles:


<product_name>.mk: makefile do produto (configurações de software da
imagem).

BoardConfig.mk: makefile da placa (configurações de hardware da imagem).

AndroidProducts.mk: makefile para registrar o produto no build system.

Outros makefiles podem ser criados para modularizar a implementação e facilitar a


manutenção do produto.
MAKEFILE DO PRODUTO
O makefile do produto tem o nome do produto (ex: superdroid.mk) e contém as
configurações de software da imagem.

Dentro deste makefile, diversas variáveis podem ser utilizadas para definir as
características do software instalado na imagem (aplicações, bibliotecas, arquivos
de configuração, etc).

Para implementá-lo, é comum utilizar a macro inherit-product para herdar algum


makefile que contenha as configurações básicas de uma imagem do Android.
O reuso de makefiles evita a duplicação de código e facilita a manutenção e a
implementação dos produtos.
SUPERDROID.MK
$(call inherit-product, device/google/cuttlefish/vsoc_x86/phone/aosp_cf.mk)

PRODUCT_NAME := superdroid
PRODUCT_DEVICE := superdroid
PRODUCT_MANUFACTURER := Embedded Labworks
PRODUCT_MODEL := Superdroid phone

PRODUCT_PACKAGES += \
SuperDroidLauncher

PRODUCT_COPY_FILES += \
$(LOCAL_PATH)/gpsreset.sh:$(TARGET_COPY_OUT_VENDOR)/etc/gpsreset.sh

PRODUCT_PACKAGE_OVERLAYS += \
vendor/labworks/superdroid/overlay
VARIÁVEIS DE PRODUTO
Variável Descrição
PRODUCT_NAME nome do produto
PRODUCT_PACKAGES adicionar componentes de software na imagem
(aplicações, bibliotecas, etc)
PRODUCT_COPY_FILES copiar arquivos para a imagem
PRODUCT_PACKAGE_OVERLAYS sobrescrever arquivos de recurso
PRODUCT_VENDOR_PROPERTIES definir propriedades customizadas
PRODUCT_CHARACTERISTICS características do dispositivo (automotive, tv, etc)
PRODUCT_MANIFEST_FILES arquivo de manifesto do produto
ADICIONANDO SOFTWARE NA IMAGEM
Variáveis que podem ser utilizadas para adicionar software na imagem:
PRODUCT_PACKAGES: adiciona software em imagens de qualquer variante.

PRODUCT_PACKAGES_DEBUG: adiciona software em imagens de variante debug.

PRODUCT_PACKAGES_ENG: adiciona software em imagens de variante eng.

Certifique-se de que o sinal de concatenação (+=) seja utilizado na atribuição!


PRODUCT_PACKAGES += \
LiveWallpapers \
Gallery2 \
SoundRecorder
ADICIONANDO ARQUIVOS NA IMAGEM
Na definição de um produto, é comum a necessidade de copiar algum arquivo
diretamente para a imagem final, como por exemplo:
Arquivos de configuração (init.rc, vold.fstab, ueventd.rc).

Executáveis e arquivos binários (firmware, shell scripts, imagem de boot, etc).

Estas customizações podem ser realizadas através da variável PRODUCT_COPY_FILES:


PRODUCT_COPY_FILES += \
vendor/labworks/superdroid/sensor.rc:vendor/etc/init/sensor.rc \
vendor/labworks/superdroid/vold.fstab:vendor/etc/vold.fstab \
$(LOCAL_PATH)/gpsreset.sh:odm/etc/gpsreset.sh
OVERLAY DE RECURSOS
Overlay de recursos é um mecanismo que possibilita sobrescrever recursos definidos
por uma aplicação sem precisar alterar o código-fonte original.

Os recursos que podem ser sobrescritos incluem arquivos de mídia, mensagens,


strings, arquivos de configuração, etc.

O overlay somente é possível em arquivos de recursos (.png, .xml, etc).


Não é possível realizar o overlay de código-fonte!

Esta funcionalidade não tem nenhuma relação com o mecanismo de overlay de


recursos em tempo de execução (Runtime Resource Overlay - RRO).
https://source.android.com/docs/core/runtime/rros
OVERLAY DE RECURSOS (CONT.)
Para realizar o overlay de recursos, é necessário criar o diretório de overlay e
adicionar os arquivos de recursos seguindo a mesma estrutura de diretórios do
código do AOSP:
$ tree vendor/labworks/superdroid/overlay/
vendor/labworks/superdroid/overlay/
├── frameworks
│ └── base
│ ├── core
│ │ └── res
│ │ └── res
│ │ ├── values
│ │ │ └── config.xml

E depois declarar o diretório de overlay no makefile do produto com a variável


PRODUCT_PACKAGE_OVERLAYS:
PRODUCT_PACKAGE_OVERLAYS := vendor/labworks/superdroid/overlay
CUSTOMIZANDO PROPRIEDADES
O Android possui o conceito de propriedades, que possibilitam coletar informações e
configurar o comportamento do sistema operacional.

Algumas variáveis estão disponíveis para definir propriedades customizadas,


incluindo PRODUCT_VENDOR_PROPERTIES e PRODUCT_PRODUCT_PROPERTIES.

As propriedades definidas nestas variáveis são adicionadas ao arquivo build.prop da


partição correspondente e carregadas no boot do sistema.

Por exemplo, incluindo propriedades no build.prop da partição product:


PRODUCT_PRODUCT_PROPERTIES += \
net.dns1=8.8.8.8 \
net.dns2=8.8.4.4
CONFIGURAÇÃO DA PLACA
Além do makefile do produto que acabamos de estudar, é necessário implementar
também o makefile da placa (BoardConfig.mk).

Este arquivo contém informações específicas do hardware (arquitetura da CPU,


bootloader, kernel, componentes de hardware, etc).

Não existe uma documentação completa sobre as variáveis que podem ser definidas
neste arquivo, e na maioria das vezes é necessário estudar o código-fonte do sistema
de build para entender seu funcionamento.

Para implementar este arquivo, a documentação do Android recomenda utilizar um


BoardConfig.mk de outro produto como referência.
https://source.android.com/docs/setup/create/new-device#makefiles
EXEMPLO: BOARDCONFIG.MK
TARGET_BOARD_PLATFORM := vsoc_x86
TARGET_ARCH := x86
TARGET_ARCH_VARIANT := x86
TARGET_CPU_ABI := x86

TARGET_NATIVE_BRIDGE_ARCH := arm
TARGET_NATIVE_BRIDGE_ARCH_VARIANT := armv7-a-neon
TARGET_NATIVE_BRIDGE_CPU_VARIANT := generic
TARGET_NATIVE_BRIDGE_ABI := armeabi-v7a armeabi

BOARD_USES_GENERIC_KERNEL_IMAGE := true
BOARD_KERNEL_CMDLINE += printk.devkmsg=on

BOARD_USES_VENDORIMAGE := true
BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE := $(TARGET_RO_FILE_SYSTEM_TYPE)

BOARD_USES_GENERIC_AUDIO := false
USE_CAMERA_STUB := true

BOARD_HAVE_BLUETOOTH := true
...
REGISTRANDO O PRODUTO
Para registrar o produto no sistema de build do Android, é necessário criar o arquivo
AndroidProducts.mk com as seguintes variáveis:
PRODUCT_MAKEFILES: localização do arquivo do makefile do produto.

COMMON_LUNCH_CHOICES: nome do produto para adicionar no lunch (opcional).

Exemplo de AndroidProducts.mk:

PRODUCT_MAKEFILES := \
$(LOCAL_DIR)/superdroid.mk

COMMON_LUNCH_CHOICES := \
superdroid-eng \
superdroid-user
RESUMO
Criar um diretório em device/<company>/<product> ou vendor/<company>/<product>
e implementar os seguintes arquivos:
<product_name>.mk: configuração da imagem do sistema operacional
(software).

BoardConfig.mk: configuração da placa (hardware).

AndroidProducts.mk: registro do produto no sistema de build.

Uma documentação (incompleta e desatualizada) está disponível no site do projeto:


https://source.android.com/setup/develop/new-device
LABORATÓRIO 4

CRIAÇÃO E CUSTOMIZAÇÃO DE PRODUTOS


ANDROID EMBARCADO

CAMADA NATIVA
A CAMADA NATIVA DO ANDROID
A CAMADA NATIVA
A camada nativa do Android é composta por todos os componentes do espaço de
usuário que rodam fora da máquina virtual:
Bibliotecas (incluindo a biblioteca C do sistema - Bionic).

Ferramentas e utilitários de linha de comando.

Mecanismo de inicialização (processo init).

Camada de abstração de hardware (HAL).

Daemons.

Veremos nesta seção do treinamento que boa parte da camada nativa do sistema
operacional foi praticamente escrita do zero para o Android!
BIBLIOTECAS
O Android possui centenas de bibliotecas que podem ser utilizadas pelas aplicações,
divididas basicamente em 3 categorias:
Bibliotecas populares em sistemas Linux (boringssl, libjpeg-turbo, sqlite, etc).

Bibliotecas reimplementadas com o objetivo de deixar o código mais leve e


simples de usar (tinyalsa, tinyxml2, etc).

Bibliotecas específicas do Android (libbinder, libutils, liblog, etc).

O código-fonte das bibliotecas está disponível nos diretórios bionic/, system/,


external/ e frameworks/native/libs.
BIBLIOTECA C
Um dos principais componentes de um sistema baseado no kernel Linux é a
biblioteca C.

A biblioteca C implementa a API do sistema operacional, provendo uma interface


para as aplicações acessarem os serviços do kernel através de chamadas de sistema.

Diversas bibliotecas C estão disponíveis para sistemas Linux, incluindo glibc, uclibc-
ng e musl.

O Android possui a sua própria biblioteca C, a Bionic!


BIONIC
Três fatores motivaram o Google a implementar sua própria biblioteca C: licença,
performance e tamanho.

A implementação da Bionic é simples, leve e liberada sob licença BSD (código-fonte


em bionic/).

Não possui suporte total ao padrão POSIX, o que pode dificultar o porte de
aplicações Linux nativas para o Android.
platform/bionic/+/refs/tags/android-13.0.0_r44/docs/status.md

Mais informações sobre a Bionic:


https://en.wikipedia.org/wiki/Bionic_(software)
BUSYBOX
Um sistema Linux requer diversas aplicações básicas como um programa init, um
shell e utilitários para manipular e configurar o sistema.

Normalmente, estas aplicações são providas separadamente por diversos pacotes


open source (coreutils, bash, grep, sed, tar, wget, modutils, etc), porém:
Estes pacotes não foram desenvolvidos com o foco em economia de recursos.

Seria bastante trabalhoso compilar separadamente cada um destes pacotes.

Em sistemas Linux, o BusyBox é uma solução para este problema, integrando


diversas ferramentas e aplicações em um único pacote, com foco em economia de
recursos.
COMANDOS DO BUSYBOX
addgroup, adduser, adjtimex, ar, arp, arping, ash, awk, basename, bbconfig, bbsh, brctl, bunzip2,
busybox, bzcat, bzip2, cal, cat, catv, chat, chattr, chcon, chgrp, chmod, chown, chpasswd, chpst,
chroot, chrt, chvt, cksum, clear, cmp, comm, cp, cpio, crond, crontab, cryptpw, cttyhack, cut,
date, dc, dd, deallocvt, delgroup, deluser, depmod, devfsd, df, dhcprelay, diff, dirname, dmesg,
dnsd, dos2unix, dpkg, dpkg_deb, du, dumpkmap, dumpleases, e2fsck, echo, ed, egrep, eject, env,
envdir, envuidgid, ether_wake, expand, expr, fakeidentd, false, fbset, fbsplash, fdflush,
fdformat, fdisk, fetchmail, fgrep, find, findfs, fold, free, freeramdisk, fsck, fsck_minix,
ftpget, ftpput, fuser, getenforce, getopt, getsebool, getty, grep, gunzip, gzip, halt, hd, hdparm,
head, hexdump, hostid, hostname, httpd, hush, hwclock, id, ifconfig, ifdown, ifenslave, ifup,
inetd, init, inotifyd, insmod, install, ip, ipaddr, ipcalc, ipcrm, ipcs, iplink, iproute, iprule,
iptunnel, kbd_mode, kill, killall, killall5, klogd, lash, last, length, less, linux32, linux64,
linuxrc, ln, load_policy, loadfont, loadkmap, logger, login, logname, logread, losetup, lpd, lpq,
lpr, ls, lsattr, lsmod, lzmacat, makedevs, man, matchpathcon, md5sum, mdev, mesg, microcom, mkdir,
mke2fs, mkfifo, mkfs_minix, mknod, mkswap, mktemp, modprobe, more, mount, mountpoint, msh, mt, mv,
nameif, netstat, nice, nmeter, nohup, nslookup, od, openvt, parse, passwd, patch, pgrep, pidof,
ping, ping6, pipe_progress, pivot_root, pkill, poweroff, printenv, printf, ps, pscan, pwd,
raidautorun, rdate, rdev, readahead, readlink, readprofile, realpath, reboot, renice, reset,
restorecon, rm, rmdir, rmmod, route, rpm, rpm2cpio, rtcwake, run_parts, runcon, runlevel, runsv,
runsvdir, rx, script, sed, selinuxenabled, sendmail, seq, sestatus, setarch, setconsole,
setfiles, setfont, setkeycodes, setlogcons, setsebool, setsid, setuidgid, sh, sha1sum, showkey,
slattach, sleep, softlimit, sort, split, start_stop_daemon, stat, strings, stty, su, sulogin, sum,
sv, svlogd, swapoff, swapon, switch_root, sync, sysctl, syslogd, tac, tail, tar, taskset, tcpsvd,
tee, telnet, telnetd, test, tftp, tftpd, time, top, touch, tr, traceroute, true, tty, ttysize,
tune2fs, udhcpc, udhcpd, udpsvd, umount, uname, uncompress, unexpand, uniq, unix2dos, unlzma,
TOOLBOX E TOYBOX
Por questões de licença, ao invés do BusyBox, o Android utiliza as ferramentas
Toolbox e Toybox, ambas liberadas sob licença BSD.

O Toolbox é uma ferramenta implementada pelo Google e seu código-fonte está


disponível em system/core/toolbox/.

O Toybox é uma ferramenta implementada pela comunidade e seu código-fonte está


disponível em external/toybox/.

Estas ferramentas possuem algumas limitações, e por este motivo é comum instalar
o BusyBox em um sistema Android, principalmente durante o desenvolvimento.
COMANDOS TOOLBOX E TOYBOX
# toolbox
getprop modprobe setprop start stop toolbox

# toybox
[ acpi base64 basename blkdiscard blkid blockdev cal cat chattr chcon chgrp chmod
chown chroot chrt cksum clear cmp comm cp cpio cut date dd devmem df diff dirname
dmesg dos2unix du echo egrep env expand expr fallocate false fgrep file find flock
fmt free freeramdisk fsfreeze fsync getconf getenforce getfattr getopt grep groups
gunzip gzip head help hostname hwclock i2cdetect i2cdump i2cget i2cset iconv id
ifconfig inotifyd insmod install ionice iorenice iotop kill killall ln load_policy
log logname losetup ls lsattr lsmod lsof lspci lsusb makedevs md5sum microcom mkdir
mkfifo mknod mkswap mktemp modinfo modprobe more mount mountpoint mv nbd-client
nc netcat netstat nice nl nohup nproc nsenter od partprobe paste patch pgrep pidof
ping ping6 pivot_root pkill pmap printenv printf prlimit ps pwd pwdx readelf readlink
realpath renice restorecon rev rfkill rm rmdir rmmod rtcwake runcon sed sendevent
seq setenforce setfattr setsid sha1sum sha224sum sha256sum sha384sum sha512sum
sleep sort split stat strings stty swapoff swapon sync sysctl tac tail tar taskset
tee test time timeout top touch tr traceroute traceroute6 true truncate tty tunctl
uclampset ulimit umount uname uniq unix2dos unlink unshare uptime usleep uudecode
uuencode uuidgen vconfig vi vmstat watch wc which whoami xargs xxd yes zcat
O PROCESSO INIT
A aplicação init é executada pelo kernel logo após montar o sistema de arquivos,
sendo responsável pela inicialização do sistema operacional.

Sistemas Linux possuem diversas implementações do processo init, dentre eles


sysvinit, systemd e upstart.

O Android possui o seu próprio sistema de inicialização!

Estudaremos em detalhes o processo de inicialização do Android mais adiante no


treinamento.
SHELL
O Android utiliza o MirBSD Korn Shell (mksh) como programa de terminal.
http://www.mirbsd.org/mksh.htm

É bem mais limitado que o shell disponível em uma distribuição Linux desktop (ex:
Bash).

Documentação pode ser acessada no código-fonte do Android com o comando


abaixo:
$ man external/mksh/src/mksh.1
DAEMONS
Os daemons são processos que rodam em background, responsáveis por gerenciar
alguma funcionalidade do sistema:
São executados na inicialização pelo processo init.

Rodam em background durante todo o tempo em que o sistema está funcional.

São normalmente utilizados para controlar e centralizar o acesso a um recurso


do sistema.

No Android, muitos daemons servem de interface entre os serviços do framework


(System Services) e os recursos providos pelo sistema operacional (rede,
armazenamento, energia, etc), abstraindo o acesso a determinado recurso.
DAEMONS EM EXECUÇÃO
# ps -A
USER PID PPID VSZ RSS WCHAN ADDR S NAME
root 1 0 10996412 9020 do_epoll_wait 0 S init
root 178 1 10828020 4568 do_sys_poll 0 S ueventd
logd 238 1 10920556 6132 sigsuspend 0 S logd
lmkd 239 1 10820664 3436 do_epoll_wait 0 S lmkd
root 244 1 10868892 7072 binder_wait_for_work 0 S vold
keystore 268 1 10942252 9996 binder_wait_for_work 0 S keystore2
tombstoned 304 1 10814252 2008 do_epoll_wait 0 S tombstoned
statsd 400 1 10888072 4256 do_epoll_wait 0 S statsd
root 401 1 10996204 10248 binder_wait_for_work 0 S netd
audioserver 481 1 11030404 18648 binder_wait_for_work 0 S audioserver
credstore 482 1 10845068 4904 binder_wait_for_work 0 S credstore
root 502 1 10863736 5832 do_epoll_wait 0 S adbd
incidentd 509 1 10856976 4200 do_epoll_wait 0 S incidentd
root 510 1 10825908 6256 binder_wait_for_work 0 S installd
root 523 1 10775716 5080 binder_wait_for_work 0 S storaged
wifi 524 1 10955548 5504 do_epoll_wait 0 S wificond
radio 529 1 11019012 11316 binder_wait_for_work 0 S libcuttlefish-rild
mdnsr 531 1 10771572 3192 do_select 0 S mdnsd
system 532 1 10906376 4960 binder_wait_for_work 0 S gatekeeperd
root 533 1 11014796 8156 do_epoll_wait 0 S update_engine
...
UEVENTD
O ueventd é responsável pelo gerenciamento da conexão de dispositivos de
hardware no Android (device hotplugging).
É o equivalente ao udev ou mdev em sistemas Linux.

Funcionalidades do ueventd:
Carregar o firmware de dispositivos de hardware.

Gerenciar a criação e permissão de arquivos no /dev e no /sys.

Parametrizável através de arquivos de configuração:


/system/etc/ueventd.rc
/vendor/etc/ueventd.rc
/odm/etc/ueventd.rc
/SYSTEM/ETC/UEVENTD.RC
firmware_directories /etc/firmware/ /odm/firmware/ /vendor/firmware/ /firmware/image/

# <path> <permission> <user> <group>


/dev/uhid 0660 uhid uhid
/dev/uinput 0660 uhid uhid
/dev/rtc0 0640 system system
/dev/tty0 0660 root system
/dev/graphics/* 0660 root graphics
/dev/input/* 0660 root input
/dev/v4l-touch* 0660 root input
/dev/snd/* 0660 system audio
/dev/bus/usb/* 0660 root usb
/dev/mtp_usb 0660 root mtp
/dev/usb_accessory 0660 root usb
/dev/tun 0660 system vpn

# <path> <file> <permission> <user> <group>


/sys/devices/virtual/input/input* enable 0660 root input
/sys/devices/virtual/input/input* poll_delay 0660 root input
...
VOLD
O vold (Volume Daemon) é responsável por gerenciar dispositivos de
armazenamento no Android.

Dentre suas principais funcionalidades, temos:


Formatar dispositivos de armazenamento.

Montar partições automaticamente.

Gerenciar encriptação de partições, arquivos e diretórios.

Pode ser parametrizado através de arquivos do tipo fstab.


RILD
O rild (Radio Interface Layer Daemon) é responsável por gerenciar a comunicação
com o chip do modem (voz e dados).

Possui diversas funcionalidades, incluindo:


Fazer e receber ligações.

Enviar e receber SMS, MMS, etc.

Estabelecer comunicação de dados (GSM, GPRS, etc).

Se comunica com o hardware de rádio através de uma biblioteca implementada pelo


fabricante (Vendor RIL).
NETD
O netd (Network Management Service Daemon) é responsável pelo gerenciamento de
conexões de rede no Android.

Dentre suas principais responsabilidades, temos:


Gerenciar a configuração de interfaces de rede.

Detectar e estabelecer novas conexões de rede.

Resolver pedidos de DNS.

Configurar tethering.

Fazer uma conexão PPP.


INSTALLD
O installd (Install Daemon) é responsável pelo gerenciamento da instalação de
aplicações Android (.apk), incluindo:
Instalação e desinstalação de aplicações.

Remoção de dados e cache das aplicações.

Compilação do bytecode DEX em um binário (dex2oat) quando a máquina virtual


ART está habilitada.

Verificação da integridade dos arquivos APK.

Coletar informações sobre aplicativos instalados.


LOGD
O logd (Log Daemon) é responsável pelos gerenciamento dos logs do Android.

O acesso aos logs é feito através de sockets exportados em /dev/socket/.


# ls /dev/socket/log*
/dev/socket/logd /dev/socket/logdr /dev/socket/logdw

Para ler ou escrever nos logs, não é necessário acessar diretamente estes sockets.
Para isso, as aplicações podem utilizar a biblioteca liblog.

Em um terminal de comandos, o usuário pode escrever no log com o comando log e


acessar os logs através da ferramenta logcat.
LOGCAT
# logcat
08-06 21:48:32.325 1814 1934 D WifiClientModeManager: entering IdleState
08-06 21:48:32.326 1573 1573 I android.hardware.wifi@1.0-service: Wifi HAL started
08-06 21:48:32.327 1814 1934 I WifiVendorHal: Vendor Hal started successfully
08-06 21:48:32.327 1814 1936 I WifiP2pNative: Registering for interface available listener
08-06 21:48:32.328 1814 1936 D WifiP2pNative: P2P InterfaceAvailableListener true
08-06 21:48:32.343 15642 15642 I wpa_supplicant: Processing hidl events on FD 3
08-06 21:48:32.343 15642 15642 I wpa_supplicant: Successfully initialized wpa_supplicant
08-06 21:48:32.344 15642 15642 I wpa_supplicant: rfkill: Cannot open RFKILL control device
08-06 21:48:32.423 15642 15642 I wpa_supplicant: rfkill: Cannot get wiphy information
08-06 21:48:32.432 1573 1573 I android.hardware.wifi@1.0-service: Configured chip in mode 0
08-06 21:48:32.433 1814 1934 D WificondControl: Setting up interface for client mode
08-06 21:48:32.434 1695 1695 I wificond: create scanner for interface with index: 3
08-06 21:48:32.434 1695 1695 I wificond: subscribe scan result for interface with index: 3
08-06 21:48:32.434 1695 1695 E wificond: No Offload Service available
08-06 21:48:32.434 1695 1695 I wificond: Offload HAL not supported
08-06 21:48:32.435 1814 1934 I chatty : uid=1000(system) WifiStateMachin identical 8 lines
08-06 21:48:32.436 1693 1738 D CommandListener: Clearing all IP addresses on wlan0
08-06 21:48:32.437 1814 1935 I chatty : uid=1000(system) WifiScanningSer expire 2 lines
08-06 21:48:32.437 1814 1934 I chatty : uid=1000(system) WifiStateMachin expire 2 lines
08-06 21:48:32.437 1814 1814 I chatty : uid=1000 system_server expire 2 lines
08-06 21:48:32.437 1814 1934 I chatty : uid=1000(system) WifiStateMachin expire 4 lines
08-06 21:48:32.437 1814 1935 I chatty : uid=1000(system) WifiScanningSer expire 1 line
...
PRIORIDADE E TAGS
Os logs são exibidos por prioridade e tag (identificador do processo):
08-06 21:48:32.434 1695 1695 I wificond: Offload HAL not supported

Diferentes prioridades estão disponíveis:


# logcat --help
...
where <tag> is a log component tag (or * for all) and priority is:
V Verbose (default for <tag>)
D Debug (default for '*')
I Info
W Warn
E Error
F Fatal
S Silent (suppress all output)
...
FILTRANDO OS LOGS
Filtrando os logs por tag:
# logcat -s ActivityManager

Filtrando os logs por prioridade:


# logcat -s *:E

Filtrando os logs por tag e prioridade:


# logcat -s ActivityManager:W
BUFFERS DE LOG
Os logs também podem ser filtrados por buffer:
# logcat -b radio

Os seguintes buffers estão disponíveis:


main: log principal, utilizado principalmente pelas aplicações.

system: logs do sistema (camada nativa e framework Android).

events: logs do sistema (eventos do framework).

radio: logs da camada de rádio.

crash: logs de crashes de aplicações.

all: exibe todos os buffers.


OPÇÕES INTERESSANTES
Exibindo as linhas mais recentes:
# logcat -t 10

Limpando todo o buffer de log:


# logcat -c

Exibindo o menu de ajuda:


# logcat -h
TOMBSTONED
O tombstoned é um daemon de depuração do Android.

É invocado pelo linker da Bionic para fazer análise postmortem de um processo que
finalizou a execução de forma inesperada.

Gera arquivos de tombstone em /data/tombstones/ para posterior análise.


# ls /data/tombstones/
tombstone_00 tombstone_01 tombstone_02
OUTROS DAEMONS
storaged: coleta e publica estatísticas de acesso a dispositivos de armazenamento.
https://source.android.com/devices/tech/debug/storaged

lmkd: gerencia o consumo de memória no Android.


https://source.android.com/devices/tech/perf/lmkd

wificond: configuração de dispositivos WiFi via commands nl80211.


https://source.android.com/devices/tech/connect/wifi-overview

adbd: gerencia as conexões ADB.

incidentd: gera relatórios de incidentes (erros) em serviços do framework.


COMANDOS DA CAMADA NATIVA
log: faz o log de uma mensagem no sistema de logs do Android.

logwrapper: permite redirecionar a saída de um comando para os logs do Android.

logcat: exibe os logs do sistema.

getevent: monitora os eventos de dispositivos de entrada (teclado, touchscreen,


mouse, botão, etc).

sendevent: gera um evento de entrada para o sistema.


COMANDOS DA CAMADA NATIVA (CONT.)
debuggerd: gera um tombstone de qualquer processo em execução.

librank: exibe o uso de bibliotecas por processo em execução.

procrank: exibe uma lista de processos ordenada por consumo de memória.

screencap: captura a imagem do dispositivo para um arquivo.

Teremos contato com mais ferramentas de linha de comando da camada nativa e do


framework no decorrer do treinamento.
LABORATÓRIO 5

MANIPULANDO A CAMADA NATIVA


ANDROID EMBARCADO

PROCESSO DE INICIALIZAÇÃO
VISÃO GERAL DO BOOT
A INICIALIZAÇÃO NO LINUX
Após montar o rootfs, o kernel irá executar uma aplicação de inicialização, também
chamado de programa init.

O programa init pode ser passado como parâmetro na linha de comandos do kernel,
através do parâmetro init (ex: init=/sbin/init).

Se o parâmetro init não for passado, o kernel tenta executar os seguintes programas:
/sbin/init, /etc/init, /bin/init e /bin/sh.

Se nenhum programa de inicialização for encontrado, o kernel entra em pânico!


Kernel panic - not syncing: No init found.
O PROGRAMA INIT
Existem diferentes implementações do programa init, incluindo:
systemd: popular, utilizado na maioria das distribuições Linux.

System V Init (sysvinit): herança de sistemas Unix, já foi usado bastante em


desktop/servidor, ainda popular em embarcados.

openrc: utilizado por padrão em algumas distribuições como o Gentoo.

upstart: desenvolvido pela Canonical para o Ubuntu, descontinuado.

O Android possui seu próprio mecanismo de inicialização!


INIT DO ANDROID
No Android, o programa init está localizado em /system/bin/init:
# cat /proc/cmdline
... init=/init ...

# ls -l /init
lrwxr-x--- 1 root shell 16 2023-05-04 18:59 /init -> /system/bin/init

Possui 3 principais responsabilidades:


Inicializar o ambiente de execução do sistema operacional.

Iniciar os daemons (init services) e gerenciar sua execução.

Gerenciar as propriedades do sistema.

O comportamento do processo init é definido em arquivos com extensão .rc.


/SYSTEM/ETC/INIT/HW/INIT.RC
import /init.environ.rc
import /system/etc/init/hw/init.usb.rc
import /init.${ro.hardware}.rc
import /vendor/etc/init/hw/init.${ro.hardware}.rc
import /system/etc/init/hw/init.usb.configfs.rc
import /system/etc/init/hw/init.${ro.zygote}.rc

# Cgroups are mounted right before early-init using list from /etc/cgroups.json
on early-init
# Disable sysrq from keyboard
write /proc/sys/kernel/sysrq 0

# Android doesn't need kernel module autoloading, and it causes SELinux


# denials. So disable it by setting modprobe to the empty string. Note: to
# explicitly set a sysctl to an empty string, a trailing newline is needed.
write /proc/sys/kernel/modprobe \n

# Set the security context of /adb_keys if present.


restorecon /adb_keys

# Set the security context of /postinstall if present.


restorecon /postinstall
...
ARQUIVOS .RC
A configuração do init é dividida em diversos arquivos .rc, distribuídos nas diversas
partições do sistema operacional e lidos na ordem abaixo:
/system/etc/init/hw/init.rc
/system/etc/init/*.rc
/system_ext/etc/init/*.rc
/vendor/etc/init/*.rc*
/odm/etc/init/*.rc*
/product/etc/init/*.rc*

Além disso, um arquivo .rc pode incluir outros arquivos .rc através da diretiva import:
import /init.environ.rc
import /system/etc/init/hw/init.usb.rc
import /init.${ro.hardware}.rc
...
SYSTEM/CORE/INIT/INIT.CPP
1 static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) {
2 Parser parser = CreateParser(action_manager, service_list);
3
4 std::string bootscript = GetProperty("ro.boot.init_rc", "");
5 if (bootscript.empty()) {
6 parser.ParseConfig("/system/etc/init/hw/init.rc");
7 if (!parser.ParseConfig("/system/etc/init")) {
8 late_import_paths.emplace_back("/system/etc/init");
9 }
10 parser.ParseConfig("/system_ext/etc/init");
11 if (!parser.ParseConfig("/vendor/etc/init")) {
12 late_import_paths.emplace_back("/vendor/etc/init");
13 }
14 if (!parser.ParseConfig("/odm/etc/init")) {
15 late_import_paths.emplace_back("/odm/etc/init");
16 }
17 if (!parser.ParseConfig("/product/etc/init")) {
18 late_import_paths.emplace_back("/product/etc/init");
19 }
20 } else {
21 parser.ParseConfig(bootscript);
22 }
23 }
SINTAXE DOS ARQUIVOS RC
Os arquivos de configuração do init possuem basicamente dois tipos de declaração:
ação e serviço.

Uma declaração de ação tem o seguinte formato:


on <trigger> [&& <trigger>]
<command>
<command>
...

Uma declaração de serviço tem o seguinte formato:


service <name> <pathname> [ <argument> ]*
<option>
<option>
...
AÇÕES E SERVIÇOS
Apenas a declaração de ação resulta na execução de comandos pelo processo init.

A declaração de serviço serve apenas para descrever o serviço.


Para executá-lo, é necessário utilizar os comandos start ou class_start dentro de
alguma declaração de ação.

Existem dois tipos de trigger que podem disparar uma ação:


Triggers de eventos: disparados via comando trigger ou função
QueueEventTrigger() no código do init.

Triggers de propriedade: disparados quando o valor de uma propriedade é


alterada.
TRIGGERS DISPARADOS NO BOOT
Após interpretar os arquivos rc, o processo init (via função QueueEventTrigger()) irá
disparar três triggers de boot:
early-init

init

late-init

O disparo destes triggers irá causar a execução dos comandos definidos na


declaração de ação correspondente, iniciando o processo de boot do Android.

Dentro de uma declaração de ação, o comando trigger pode ser utilizado para
disparar outras ações durante o processo de boot.
SYSTEM/CORE/INIT/INIT.CPP
1 int SecondStageMain(int argc, char** argv) {
2 ...
3 LoadBootScripts(am, sm);
4 ...
5
6 am.QueueEventTrigger("early-init");
7 ...
8
9 am.QueueEventTrigger("init");
10 ...
11
12 std::string bootmode = GetProperty("ro.bootmode", "");
13 if (bootmode == "charger") {
14 am.QueueEventTrigger("charger");
15 } else {
16 am.QueueEventTrigger("late-init");
17 }
18 ...
19 }
/SYSTEM/ETC/INIT/HW/INIT.RC
on early-init
# Disable sysrq from keyboard
write /proc/sys/kernel/sysrq 0

# Android doesn't need kernel module autoloading, and it causes SELinux


# denials. So disable it by setting modprobe to the empty string. Note: to
# explicitly set a sysctl to an empty string, a trailing newline is needed.
write /proc/sys/kernel/modprobe \n

# Set the security context of /adb_keys if present.


restorecon /adb_keys
...

on init
sysclktz 0

# Mix device-specific information into the entropy pool


copy /proc/cmdline /dev/urandom
copy /system/etc/prop.default /dev/urandom

symlink /proc/self/fd/0 /dev/stdin


symlink /proc/self/fd/1 /dev/stdout
symlink /proc/self/fd/2 /dev/stderr
...
/SYSTEM/ETC/INIT/HW/INIT.RC
on late-init
trigger early-fs

# Mount fstab in init.{$device}.rc by mount_all command. Optional parameter


# '--early' can be specified to skip entries with 'latemount'.
# /system and /vendor must be mounted by the end of the fs stage,
# while /data is optional.
trigger fs
trigger post-fs

# Mount fstab in init.{$device}.rc by mount_all with '--late' parameter


# to only mount entries with 'latemount'. This is needed if '--early' is
# specified in the previous mount_all command on the fs stage.
# With /system mounted and properties form /system + /factory available,
# some services can be started.
trigger late-fs

...

trigger early-boot
trigger boot
COMANDOS
São vários os comandos que podem ser utilizados no arquivo de configuração do
init, incluindo chdir, chmod, chown, copy, exec, export, hostname, ifup, insmod,
loglevel, mkdir, symlink, restart, mount, trigger, etc.

Perceba que, apesar da semelhança, estes não são comandos do shell!

A documentação do init, incluindo uma descrição dos principais comandos, está


disponível no código-fonte do Android.
system/core/init/README.md
SERVIÇOS DO INIT
A diretiva service permite declarar serviços do init (ex: programas que serão
executados automaticamente no boot do Android).

Ao declarar um serviço, diversas opções (options) podem ser utilizadas para


parametrizar a execução do serviço.

A diretiva service apenas descreve o serviço, mas não o inicia automaticamente.

A execução de um serviço é controlada através dos comandos start, stop, class_start,


class_reset e class_stop.
DECLARAÇÃO DE SERVIÇOS
service surfaceflinger /system/bin/surfaceflinger
class core animation
user system
group graphics drmrpc readproc
capabilities SYS_NICE
onrestart restart --only-if-running zygote
task_profiles HighPerformance
socket pdx/system/vr/display/client
socket pdx/system/vr/display/manager
socket pdx/system/vr/display/vsync

service bootanim /system/bin/bootanimation


class core animation
user graphics
group graphics audio
disabled
oneshot
ioprio rt 0
task_profiles MaxPerformance
OPÇÕES DOS SERVIÇOS
Opção Descrição
class classe do serviço
user usuário do serviço
group grupo(s) do serviço
socket cria um UNIX domain socket e passa o descritor para o serviço
disabled não inicia automaticamente o serviço pela classe
critical reinicia se o serviço encerrar a execução mais de 4 vezes em 4 minutos
onrestart executa um comando ao reiniciar o serviço
oneshot executa o serviço apenas uma vez
INICIANDO OS SERVIÇOS
on early-init
...
start ueventd

on init
...
start servicemanager
start hwservicemanager
start vndservicemanager

on boot
...
class_start hal
class_start core

on nonencrypted
class_start main
class_start late_start
PROPRIEDADES
As propriedades no Android são variáveis globais compartilhadas e acessíveis por
todo o sistema operacional.
Na prática, são um dicionário global de strings no formato chave=valor.

Utilizadas para armazenar informações e configurações do sistema operacional.

As propriedades são inicializadas no boot do Android via:


Leitura de arquivos com extensão .prop.

Execução de comandos setprop.

As propriedades são mantidas em memória RAM durante a execução do sistema


operacional.
ARQUIVOS .PROP
Boa parte das propriedades são declaradas em arquivos gerados durante o build do
Android e carregados no boot do sistema:

/system/etc/build.prop
/system/default.prop
/system/build.prop
/system_ext/etc/build.prop
/system_dlkm/etc/build.prop
/vendor/etc/build.prop
/vendor/default.prop
/vendor/build.prop
/vendor_dlkm/etc/build.prop
/odm/etc/build.prop
/odm_dlkm/etc/build.prop
/product/etc/build.prop
/data/local.prop

Veja a função PropertyLoadBootDefaults() no código do init em property_service.cpp.


MANIPULANDO AS PROPRIEDADES
As propriedades podem ser manipuladas no terminal via comandos getprop/setprop,
e nas aplicações através de APIs providas pelo sistema operacional.

A leitura das propriedades é feita através de um mecanismo inteligente de


compartilhamento de memória.

A escrita em propriedades é controlada pelo processo init através de um UNIX


domain socket:
# ls -l /dev/socket/property_service
srw-rw-rw- 1 root root 0 2023-05-18 12:29 /dev/socket/property_service
GETPROP
O comando getprop pode ser utilizado para listar as propriedades existentes:

# getprop
[apexd.status]: [ready]
[bluetooth.device.class_of_device]: [90,2,12]
[bluetooth.profile.a2dp.source.enabled]: [true]
[bluetooth.profile.asha.central.enabled]: [true]
[bluetooth.profile.avrcp.target.enabled]: [true]
[bluetooth.profile.bap.broadcast.assist.enabled]: [true]
[bluetooth.profile.bap.unicast.client.enabled]: [true]
[bluetooth.profile.bas.client.enabled]: [true]
[bluetooth.profile.ccp.server.enabled]: [true]
[bluetooth.profile.csip.set_coordinator.enabled]: [true]
[bluetooth.profile.gatt.enabled]: [true]
[bluetooth.profile.hap.client.enabled]: [true]
[bluetooth.profile.hfp.ag.enabled]: [true]
[bluetooth.profile.hid.device.enabled]: [true]
[bootreceiver.enable]: [1]
[bpf.progs_loaded]: [1]
[build.version.extensions.r]: [3]
[build.version.extensions.s]: [3]
[build.version.extensions.t]: [3]
...
SETPROP
O comando setprop pode ser utilizado para alterar uma propriedade:
# setprop labworks.teste 1

Por padrão, as propriedades são criadas em memória. Caso queira criar uma
propriedade persistente, coloque persist no nome da propriedade.
# setprop persist.labworks.teste 1

As propriedades persistentes são armazenadas em /data/property/ e carregadas


automaticamente no boot do Android:

# ls -l /data/property/
-rw------- 1 root root 617 2023-05-18 12:29 persistent_properties
PROPRIEDADES ESPECIAIS
Algumas propriedades possuem um significado especial:
persist.*: propriedades persistentes armazenadas em /data/property/.

ro.*: propriedades de apenas leitura (uma vez criadas, não podem ser alteradas).

ro.boottime.*: estatísticas de tempo de boot.

init.svc.<name>: propriedades que indicam o estado de um serviço do init.

ctl.start: propriedade especial usada para iniciar um serviço.

ctl.stop: propriedade especial usada para terminar um serviço.


TRIGGER DE PROPRIEDADE
O processo init é capaz de tomar ações baseadas em mudança de propriedade:

on property:sys.boot_completed=1
bootchart stop
exec - system system -- /bin/rm -rf /data/per_boot
mkdir /data/per_boot 0700 system system encryption=Require key=per_boot_ref

on property:persist.vendor.net.doxlat=true
setprop net.464xlat.cellular.enabled true

on property:sys.usb.config=none && property:sys.usb.configfs=0


stop adbd
write /sys/class/android_usb/android0/enable 0
write /sys/class/android_usb/android0/bDeviceClass 0
setprop sys.usb.state ${sys.usb.config}

on property:sys.init_log_level=*
loglevel ${sys.init_log_level}
O BOOT DO FRAMEWORK
O programa init é responsável pela inicialização da camada nativa do Android, mas
não gerencia o framework.

A inicialização do framework é feita por um serviço chamado zygote, executado pelo


init através de um programa chamado app_process.
O app_process é um programa capaz de executar bytecode DEX em um ambiente
de máquina virtual Dalvik ou ART.

O zygote é o programa responsável por inicializar o framework e o ambiente de


execução de aplicações do Android.
APP_PROCESS E ZYGOTE
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
class main
priority -20
user root
group root readproc reserved_disk
socket zygote stream 660 root system
socket usap_pool_primary stream 660 root system
onrestart exec_background - system system -- /system/bin/vdc volume abort_fuse
onrestart write /sys/power/state on
onrestart restart audioserver
onrestart restart cameraserver
onrestart restart media
onrestart restart media.tuner
onrestart restart netd
onrestart restart wificond
task_profiles ProcessCapacityHigh
critical window=${zygote.critical_window.minute:-off} target=zygote-fatal
ZYGOTE
O zygote é um dos principais daemons do Android, sendo responsável por:
Pré-carregar todas as classes Java em memória.

Iniciar o system server (daemon de serviços do framework).

Esperar conexões em um UNIX domain socket (/dev/socket/zygote) para iniciar


novas aplicações Android.

Caso o sistema seja compilado para suportar múltiplas ABIs (ex: 32 e 64 bits), é
possível ter mais de um processo do zygote em execução:
# ps -A | grep zygote
root 395 1 14083636 165372 do_sys_poll 0 S zygote64
nobody 396 1 1630100 119300 futex_wait_queue_me 0 S zygote
SYSTEM SERVER
O system server é um processo separado executado pelo Zygote durante sua
inicialização.

Este processo é responsável por registrar diversos serviços do framework, que


podem ser utilizados pelas aplicações através da API do Android.
Um destes serviços registrados é o Activity Manager Service.

Ao fim de sua inicialização, o Activity Manager Service envia um Intent do tipo


CATEGORY_HOME, fazendo com que a aplicação Launcher seja executada.
DIAGRAMA: INICIALIZAÇÃO DO ANDROID
PROCESSOS NA INICIALIZAÇÃO
USER PID PPID VSZ RSS WCHAN ADDR S NAME
root 1 0 10885820 10628 do_epoll_wait 0 S init
root 395 1 14078208 162648 do_sys_poll 0 S zygote
system 637 395 14888480 319412 do_epoll_wait 0 S system_server
u0_a78 868 395 13674004 217768 do_epoll_wait 0 S com.android.systemui
webview_zygote 919 396 1903184 89456 do_sys_poll 0 S webview_zygote
network_stack 931 395 13520300 116112 do_epoll_wait 0 S com.android.networkstack.process
bluetooth 936 395 13618384 132192 do_epoll_wait 0 S com.android.bluetooth
secure_element 955 395 13507784 96464 do_epoll_wait 0 S com.android.se
radio 957 395 13581824 139320 do_epoll_wait 0 S com.android.phone
system 968 395 13586948 147756 do_epoll_wait 0 S com.android.settings
u0_a88 1011 395 13507692 99448 do_epoll_wait 0 S android.ext.services
u0_a83 1186 395 13517028 112732 do_epoll_wait 0 S com.android.cellbroadcastreceiver
u0_a56 1222 395 13525044 108024 do_epoll_wait 0 S com.android.dialer
u0_a74 1311 395 13551964 160108 do_epoll_wait 0 S com.android.launcher3
u0_a85 1355 395 13527612 109628 do_epoll_wait 0 S com.android.permissioncontroller
u0_a68 1356 395 13573372 115272 do_epoll_wait 0 S com.android.inputmethod.latin
u0_a79 1403 395 13555368 123836 do_epoll_wait 0 S com.android.providers.media.module
u0_a67 1447 395 13513964 106812 do_epoll_wait 0 S com.android.deskclock
u0_a76 1466 395 13506396 94124 do_epoll_wait 0 S com.android.carrierconfig
...
LABORATÓRIO 6

INICIALIZAÇÃO DO ANDROID
ANDROID EMBARCADO

MÓDULOS DE COMPILAÇÃO
MÓDULOS
Todo componente de software do AOSP (aplicação nativa, aplicação Java,
biblioteca, etc) é chamado de módulo pelo sistema de build do Android.

As instruções para compilar um módulo podem ser definidas em um makefile


(Android.mk) ou em um arquivo de Blueprint (Android.bp).

O uso de makefiles foi descontinuado nas últimas versões do Android, e os arquivos


de Blueprint se tornaram padrão.

De qualquer forma, nesta seção do treinamento estudaremos os dois formatos de


arquivo de compilação.
MAKEFILES
O Android é capaz de compilar módulos através de makefiles.

Para isso, basta criar um arquivo chamado Android.mk com os parâmetros de


compilação na raiz do código-fonte do módulo.

Não é necessário definir comandos de compilação neste arquivo, apenas variáveis


que serão utilizadas para parametrizar a compilação do módulo.

A ferramenta kati é responsável por processar o Android.mk e gerar um arquivo .ninja


de compilação.

Uma documentação (desatualizada) sobre o Android.mk está disponível no código-


fonte do Android em build/core/build-system.html.
EXEMPLO: ANDROID.MK
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

LOCAL_MODULE := myapp
LOCAL_SRC_FILES := myapp.c

LOCAL_SHARED_LIBRARIES := liblog
LOCAL_CFLAGS := -O0 -Wall

LOCAL_VENDOR_MODULE := true

include $(BUILD_EXECUTABLE)
TEMPLATE: ANDROID.MK
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

LOCAL_VARIABLE_1 := value_1
LOCAL_VARIABLE_2 := value_2
LOCAL_VARIABLE_3 := value_3
...

include $(BUILD_MODULE_TYPE)
ANDROID.MK: PRINCIPAIS VARIÁVEIS
Variável Descrição
LOCAL_SRC_FILES Lista de arquivos-fonte que serão compilados
LOCAL_MODULE Nome do módulo no sistema de build
LOCAL_PACKAGE_NAME Nome da aplicação Android (.apk)
LOCAL_SHARED_LIBRARIES Lista de bibliotecas nativas que o módulo depende
LOCAL_C_INCLUDES Caminhos adicionais de arquivos de cabeçalho usados
pelo módulo
LOCAL_JAVA_LIBRARIES Lista de bibliotecas Java que o módulo depende
ANDROID.MK: PRINCIPAIS VARIÁVEIS (CONT.)
Variável Descrição
LOCAL_CFLAGS Flags extras para os compiladores C e C++
LOCAL_CPPFLAGS Flags extras para o compilador C++
LOCAL_LDFLAGS Flags extras para o linker
LOCAL_MODULE_CLASS Indica o tipo de módulo (ETC, APPS, EXECUTABLES, etc)
LOCAL_MODULE_PATH Local de instalação do módulo
LOCAL_VENDOR_MODULE Força a instalação do módulo na partição /vendor
ANDROID.MK: TIPOS DE BUILD
Macro Descrição
BUILD_EXECUTABLE Compila e gera um executável no formato ELF
BUILD_SHARED_LIBRARY Compila e gera uma biblioteca dinâmica (.so)
BUILD_STATIC_LIBRARY Compila e gera uma biblioteca estática (.a)
BUILD_JAVA_LIBRARY Compila e gera uma biblioteca Java (.jar)
BUILD_PACKAGE Compila e gera uma aplicação Android (.apk)
BUILD_PREBUILT Instala um arquivo ou binário pré-compilado
ANDROID.MK: MACROS
Diversas macros estão disponíveis para uso em um Android.mk:
all-subdir-c-files: retorna todos os arquivos C recursivamente a partir do diretório
corrente.
LOCAL_SRC_FILES := $(call all-subdir-c-files)

all-java-files-under: retorna todos os arquivos java recursivamente a partir do


diretório passado como parâmetro.
LOCAL_SRC_FILES := $(call all-java-files-under, src)

A maioria das macros estão definidas em build/core/definitions.mk.


ARQUIVOS DE BLUEPRINT
Atualmente, arquivos de Blueprint são o mecanismo padrão de compilação de
módulos no Android.

Para compilar um módulo com o Blueprint, basta criar um arquivo chamado


Android.bp na raiz do código-fonte com os parâmetros de compilação do módulo.

Este arquivo é simples e possui uma sintaxe declarativa parecida com JSON.
A sintaxe e semântica do Blueprint é similar aos arquivos BUILD do Bazel (o que
deve facilitar a migração para o futuro build system do Android).

O sistema de build Soong é responsável por processar o Android.bp e gerar o arquivo


.ninja de compilação.
EXEMPLO: ANDROID.BP
cc_binary {
name: "myapp",
srcs: ["myapp.c"],
shared_libs: ["liblog"],
cflags: [
"-O0",
"-Wall",
],
vendor: true,
}
TEMPLATE: ANDROID.BP
cc_type {
property_1: "string",
property_2: [
"string-list-1",
"string-list-2",
],
property_3: {
key1: "value1",
key2: ["value2"],
},
property_4: true,
property_5: 42,
}
ANDROID.BP: PRINCIPAIS PROPRIEDADES
Propriedade Descrição
srcs Lista de arquivos-fonte que serão compilados
name Nome do módulo no sistema de build
shared_libs Lista de bibliotecas nativas que o módulo depende
include_dirs Caminhos adicionais de arquivos de cabeçalho usados pelo módulo
libs Lista de bibliotecas Java que o módulo depende
required Lista de módulos que um módulo depende (dependência de
execução)
ANDROID.BP: PRINCIPAIS PROPRIEDADES (CONT.)
Propriedade Descrição
export_include_dirs Diretórios com arquivos de include que serão exportados para
outros módulos
cflags Flags extras para os compiladores C e C++
cppflags Flags extras para o compilador C++
ldflags Flags extras para o linker
vendor Força a instalação do módulo na partição /vendor
ANDROID.BP: TIPOS DE BUILD
Tipo de build Descrição
cc_binary Compila e gera um executável no formato ELF
cc_library_shared Compila e gera uma biblioteca dinâmica (.so)
cc_library_static Compila e gera uma biblioteca estática (.a)
java_binary Compila e gera um executável Java
java_library Compila e gera uma biblioteca Java (.jar)
android_app Compila e gera uma aplicação Android (.apk)
cc_prebuilt_binary Instala um binário pré-compilado
FERRAMENTAS E DOCUMENTAÇÃO
É possível converter um Android.mk em Android.bp com a ferramenta androidmk:
$ androidmk Android.mk > Android.bp

O comando bpfmt pode ser utilizado para formatar um arquivo de Blueprint:


$ bpfmt -w Android.bp

Consulte o Soong Modules Reference para uma lista completa de propriedades e


tipos de build que podem ser utilizados em arquivos Blueprint.

Para mais informações sobre o sistema de build Soong:


https://android.googlesource.com/platform/build/soong/
COMPILANDO UM MÓDULO
Com o arquivo de compilação criado (Android.mk ou Android.bp), para compilar
basta executar o comando m, passando o nome do módulo:
$ m myapp

Toda a compilação do módulo acontece no diretório out/:


$ ls out/soong/.intermediates/device/labworks/superdroid/myapp/*/
myapp myapp.d obj unstripped
$ ls out/target/product/vsoc_x86/obj/EXECUTABLES/myapp_intermediates
myapp

Para limpar a compilação, basta remover os diretórios intermediários ou processar o


target clean-<module>:
$ m clean-myapp
LABORATÓRIO 7

COMPILAÇÃO DE MÓDULOS
ANDROID EMBARCADO

ANDROID HAL
ABSTRAÇÃO DE HARDWARE NO LINUX
ABSTRAÇÃO DE HARDWARE NO ANDROID
HARDWARE ABSTRACTION LAYER
Em sistemas Linux, o acesso a um dispositivo de hardware é normalmente exposto
para as aplicações através de entradas no /dev e no /sys.

O Android se baseia em uma camada adicional chamada HAL (Hardware Abstraction


Layer) para abstrair o acesso ao hardware.

A principal motivação para a existência da HAL é desacoplar o framework do código


de acesso ao hardware, facilitando a integração do código genérico do Android
(AOSP) com a plataforma de hardware do fabricante.

O Google não especifica como uma HAL deve ser desenvolvida, apenas define uma
interface padronizada que deve ser implementada pela HAL.
HAL E AS CAMADAS DO ANDROID
SENSORS HAL
BLUETOOTH HAL
GRAPHICS HAL
AUDIO HAL
CAMERA HAL
ARQUITETURA DA HAL
Até o Android 7, as HALs eram implementadas em bibliotecas compartilhadas (.so),
consumidas pelos serviços do framework via JNI.

É um design simples e funcional, porém com algumas deficiências:


A interface das HALs não é estável. A cada nova versão do Android a definição da
interface pode mudar, exigindo a reimplementação da HAL.

Há uma depêndencia de API/ABI entre os serviços do framework e as HALs. Por


exemplo, se o framework for recompilado com um toolchain diferente, é
necessário recompilar também as HALs.

Estas deficiências deixam o código do framework muito dependente da


implementação das HALs, e essa dependência dificulta o processo de atualização do
sistema operacional, fragmentando a plataforma!
DISTRIBUIÇÃO DAS VERSÕES DO ANDROID
FRAGMENTAÇÃO DA PLATAFORMA
HERE COMES TREBLE!
Em 2017, o projeto Treble foi criado para minimizar/resolver este problema.
Here comes Treble: A modular base for Android

Implementado a partir do Android 8, a idéia do projeto é estabilizar e tornar


independente a interface entre o framework do Android e a implementação do
fabricante do hardware (vendor/oem).

Com o Treble, a cada release do Android, o Google garante que a GSI (Generic System
Image) seja retrocompatível com os últimos 3 releases do sistema operacional.

Diversas alterações foram feitas na arquitetura do sistema operacional para tornar


isso possível (Binderized HALs, HIDL, VTS, VNDK, VINTF, GSI, etc).
PROJECT TREBLE
4 ANOS DE SUPORTE

Referência: Treble Plus One Equals Four


ALTERAÇÕES NA ARQUITETURA
Para estabilizar e deixar o código do framework independente do código do vendor,
as HALs se transformaram em serviços que são consumidos via Binder.

A interface das HALs é estável e definida formalmente através de HIDL (HAL Interface
Definition Language) ou AIDL (Android Interface Definition Language).
https://source.android.com/devices/architecture/hidl
https://source.android.com/docs/core/architecture/aidl

As HALs e outras customizações são distribuídas em partições separadas (/vendor,


/product, /odm), possibilitando atualizar a partição /system de forma independente.

Uma ferramenta chamada VTS (Vendor Test Suite) foi criada para testar e validar a
implementação da interface do fabricante (mesmo conceito da ferramenta CTS para
validar o framework).
CTS E VTS
COMUNICAÇÃO COM AS HALS
OUTRAS ALTERAÇÕES DO TREBLE
Padronização da versão do kernel Linux utilizado em releases oficiais do Android.

O VNDK (Vendor Native Development Kit) é um conjunto de bibliotecas exclusivas


para uso de código do fabricante do hardware. Executáveis na partição /vendor
(incluindo HALs) só podem linkar com bibliotecas do VNDK.
https://source.android.com/devices/architecture/vndk

O VINTF Object (Vendor Interface Object) permite especificar em tempo de


compilação (e recuperar em tempo de execução) informações de compatibilidade
entre o framework do Android e a implementação do vendor.
https://source.android.com/devices/architecture/vintf
IMPLEMENTAÇÃO DA HAL
Implementar uma HAL é trabalhoso e envolve diferentes áreas de conhecimento
(hardware, interface provida pelo kernel, interface da HAL definida pelo Google,
funcionamento do framework, etc).

Por este motivo, se possível escolha um dispositivo de hardware que já possua uma
implementação de HAL do Android.

Caso não seja possível, será necessário estudar o funcionamento da HAL, e


dependendo do dispositivo, ler/escrever muito código-fonte!
DOCUMENTAÇÃO E CÓDIGO-FONTE
Uma visão geral da HAL está documentada pelo Google:
https://source.android.com/docs/core/architecture

A definição da interface das HALs, incluindo algumas implementações de referência,


estão disponíveis nos seguintes diretórios do AOSP:
hardware/interfaces/
frameworks/hardware/interfaces/
system/hardware/interfaces/

Existem também algumas implementações de HALs no diretórios dos produtos em


device/.
LIGHTS HAL (HIDL)
$ tree hardware/interfaces/light/2.0/
hardware/interfaces/light/2.0/
├── Android.bp
├── default
│ ├── Android.bp
│ ├── android.hardware.light@2.0-service-lazy.rc
│ ├── android.hardware.light@2.0-service.rc
│ ├── Light.cpp
│ ├── Light.h
│ ├── service.cpp
│ └── serviceLazy.cpp
├── ILight.hal
├── types.hal
└── vts
└── functional
├── Android.bp
└── VtsHalLightV2_0TargetTest.cpp
LIGHTS HAL (AIDL)
$ tree hardware/interfaces/light/aidl/android/
hardware/interfaces/light/aidl/android/
└── hardware
└── light
├── BrightnessMode.aidl
├── FlashMode.aidl
├── HwLight.aidl
├── HwLightState.aidl
├── ILights.aidl
└── LightType.aidl

$ ls hardware/interfaces/light/aidl/Android.bp
hardware/interfaces/light/aidl/Android.bp

$ tree hardware/interfaces/light/aidl/default/
hardware/interfaces/light/aidl/default/
├── Android.bp
├── Lights.cpp
├── lights-default.rc
├── lights-default.xml
├── Lights.h
└── main.cpp
HAL DE PORTA SERIAL
Para colocar em prática todos estes conceitos, iremos desenvolver uma HAL de porta
serial.

A HAL irá prover dois métodos:


tx(): transmissão de dados para a porta serial.

rx(): recepção de dados da porta serial.

Nos próximos slides, veremos trechos de código-fonte implementando esta HAL, que
será desenvolvida por completo no próximo exercício do treinamento.
CAMADAS PARA ACESSAR A PORTA SERIAL
SERIAL PORT HAL
IMPLEMENTANDO A HAL DE PORTA SERIAL
Os seguintes passos são necessários para implementar a HAL:
Passo 1: definição da interface da HAL (HIDL ou AIDL).

Passo 2: implementação da HAL (C++, Java ou Rust).

Passo 3: declaração da HAL nos arquivos de manifesto do dispositivo e do


framework (VINTF object).

Apesar de não ser necessário, iremos implementar também uma aplicação de testes
da HAL.
DEFININDO A INTERFACE DA HAL
É possível definir a interface de uma HAL através das linguagens HIDL (Hardware
Interface Definition Language) ou AIDL (Android Interface Definition Language).
HIDL foi introduzida com o Project Treble.

AIDL é muito mais antiga e madura que a HIDL.

Com o tempo, o Google percebeu um overlap de funcionalidades entre as


linguagens.

No Android 10, as principais funcionalidades da HIDL foram implementadas na AIDL.

A partir do Android 11, tornou-se possível escrever HALs com AIDL, e o uso de HIDL
foi descontinuado.
https://source.android.com/devices/architecture/aidl/overview
DEFININDO A INTERFACE COM AIDL
Definindo a interface da HAL de porta serial com a linguagem AIDL:
// ISerialPort.aidl
interface ISerialPort {
void tx(byte data);
byte rx();
}

O arquivo de definição da interface (.aidl) será processado pela ferramenta aidl.

A ferramenta aidl irá gerar as classes de comunicação com a HAL (Proxy e Stub)
encapsuladas em uma biblioteca compartilhada (.so ou .jar):
A classe Proxy será utilizada pelo cliente para enviar a mensagem.

A classe Stub será utilizada pelo servidor (HAL) para receber a mensagem.
ARQUITETURA DE COMUNICAÇÃO VIA AIDL
IMPLEMENTANDO A HAL
A implementação da HAL pode ser feita em C++, Java ou Rust.
https://source.android.com/docs/core/architecture/aidl/aidl-backends

A partir da definição da HAL, podemos implementar sua interface, estendendo a


classe Stub e implementando seus métodos.
A ferramenta aidl é capaz de gerar um esqueleto desta implementação.

Como a HAL rodará dentro de um processo no Android, é necessário criar um


programa que irá instanciar a classe da HAL e registrá-la no servicemanager.
Um arquivo de inicialização (.rc) é necessário para rodar o programa
automaticamente no boot do Android.
IMPLEMENTAÇÃO DA CLASSE SERIALPORT
1 // SerialPort.h
2 #include <aidl/vendor/labworks/serialport/BnSerialPort.h>
3
4 class SerialPort : public BnSerialPort {
5 ndk::ScopedAStatus tx(int8_t data) override;
6 ndk::ScopedAStatus rx(int8_t* data) override;
7 };

1 // SerialPort.cpp
2 #include "SerialPort.h"
3
4 ndk::ScopedAStatus SerialPort::tx(int8_t data) {
5 // TODO: implement method
6 return ndk::ScopedAStatus::ok();
7 }
8
9 ndk::ScopedAStatus SerialPort::rx(int8_t* data) {
10 // TODO: implement method
11 return ndk::ScopedAStatus::ok();
12 }
IMPLEMENTAÇÃO DO PROGRAMA DA HAL
1 // main.cpp
2
3 #include "SerialPort.h"
4 #include <android/binder_manager.h>
5 #include <android/binder_process.h>
6
7 using ::aidl::vendor::labworks::serialport::SerialPort;
8
9 int main() {
10 ABinderProcess_setThreadPoolMaxThreadCount(16);
11
12 std::shared_ptr<SerialPort> serialport = ndk::SharedRefBase::make<SerialPort>();
13
14 const std::string instance = std::string() + SerialPort::descriptor + "/default";
15 binder_status_t status = AServiceManager_addService(serialport->asBinder().get(),
16 instance.c_str());
17 CHECK_EQ(status, STATUS_OK);
18
19 ABinderProcess_startThreadPool();
20 ABinderProcess_joinThreadPool();
21
22 return EXIT_FAILURE; // should not reach here
23 }
ARQUIVOS DE MANIFESTO
Para adicionar a HAL à imagem, é recomendado declará-la nos arquivos de
manifesto do produto através do vendor interface object (VINTF object).

O VINTF object inclui os seguintes arquivos:


Device manifest: descreve os serviços que o device provê para o framework.

Framework manifest: descreve os serviços que o framework provê para o device.

Device compatibility matrix: descreve o que o device espera do framework.

Framework compatibility matrix: descreve o que o framework espera do device.

Ao implementar uma HAL, pode ser necessário popular o device manifest e o


framework compatibility matrix.
VINTF OBJECT
TESTANDO A HAL
Para testar a HAL, podemos criar uma aplicação cliente de testes.

A aplicação pode utilizar o método estático fromBinder() da classe da HAL para obter
uma referência à classe Proxy de comunicação.

Com o ponteiro para a classe Proxy, os métodos da HAL podem ser invocados
diretamente pela aplicação de testes.
Toda a comunicação com a HAL via Binder é abstraída pela classe Proxy.
CLIENTE DE TESTES DA HAL
1 // client.cpp
2
3 int main(int argc, char *argv[])
4 {
5 const std::string instance = std::string() + ISerialPort::descriptor + "/default";
6 std::shared_ptr<ISerialPort> serialport;
7
8 serialport = ISerialPort::fromBinder(ndk::SpAIBinder(AServiceManager_checkService(
9 instance.c_str())));
10 if (!serialport) {
11 std::cout << "Error requesting serialport service handler!" << std::endl;
12 return 1;
13 }
14
15 /* TX */
16 serialport->tx('0');
17
18 /* RX */
19 int8_t data = 0;
20 serialport->rx(&data);
21
22 return 0;
23 }
HALS EM EXECUÇÃO
1 # ps -A | grep android.hardware.sensors
2 system 448 1 33676 6252 binder_wait_for_work 0 S android.hardware.sensors-service
3
4 # ps -A | grep android.hardware.bluetooth
5 bluetooth 387 1 22840 5020 binder_wait_for_work 0 S android.hardware.bluetooth@1.1-s
6
7 # lshal | grep android.hardware.bluetooth
8 DM,FC Y android.hardware.bluetooth@1.0::IBluetoothHci/default 0/1 38
9 DM,FC Y android.hardware.bluetooth@1.1::IBluetoothHci/default 0/1 38
10
11 # service list | grep android.hardware.sensors
12 51 android.hardware.sensors.ISensors/default: [android.hardware.sensors.ISensors]
13
14 # logcat | grep android.hardware.bluetooth
15 06-14 15:00:42.867 0 0 I init : Parsing file /vendor/etc/init/android.hardware.bluetoo
16 06-14 15:01:00.839 610 634 I SystemConfig: Reading permissions from /vendor/etc/permissions/a
17 06-14 15:01:00.860 610 634 I SystemConfig: Reading permissions from /vendor/etc/permissions/a
18 06-14 15:02:58.981 387 387 I android.hardware.bluetooth@1.1-btlinux: BluetoothHci::initialize
19 06-14 15:02:58.981 387 387 I android.hardware.bluetooth@1.1-btlinux: openBtHci
20 06-14 15:02:58.981 387 387 E android.hardware.bluetooth@1.1-btlinux: Unable to open /dev/rfki
21 06-14 15:02:58.981 387 387 I android.hardware.bluetooth@1.1-btlinux: waitHciDev
22 06-14 15:02:58.981 387 387 I android.hardware.bluetooth@1.1-btlinux: HCI device ready
DISPOSITIVOS SEM NECESSIDADE DE HAL
Nem todo dispositivo de hardware precisa de uma HAL no Android:
Dispositivos de entrada (botões, mouse, teclado) são acessados diretamente via
arquivos exportados pelo kernel em /dev/input/.

Dispositivos de rede são acessados normalmente via interface de rede, através de


uma conexão de socket ou via daemon netd.

Dispositivos de armazenamento (cartão SD) são acessados diretamente através


de chamadas de sistema ou via daemon vold.
LABORATÓRIO 8

IMPLEMENTAÇÃO DE UMA HAL


ANDROID EMBARCADO

ANDROID FRAMEWORK
ANDROID FRAMEWORK
FRAMEWORK
Framework é a camada responsável por prover serviços para as aplicações Android,
com três principais componentes:
Runtime da máquina virtual (ART/Zygote).

System Services (serviços do framework).

APIs (providas pelo SDK).

Contém todas as "regras de negócio" do sistema operacional.

Nesta seção do treinamento, focaremos nos serviços do framework (System


Services).
SYSTEM SERVICES
System Services são serviços responsáveis por gerenciar o acesso aos recursos
providos pelo sistema operacional. Exemplos:
Power Manager Service: gerenciamento de energia.

Sensor Service: gerenciamento de sensores.

Location Manager Service: gerenciamento de localização.

Activity Manager Service: gerenciamento dos componentes de aplicações.

Input Manager Service: gerenciamento dos dispositivos de entrada.

O Android 13 possui mais de 200 serviços!


LISTANDO OS SERVIÇOS
# service list
Found 277 services:
0 DockObserver: []
1 SurfaceFlinger: [android.ui.ISurfaceComposer]
2 SurfaceFlingerAIDL: [android.gui.ISurfaceComposer]
3 accessibility: [android.view.accessibility.IAccessibilityManager]
4 account: [android.accounts.IAccountManager]
5 activity: [android.app.IActivityManager]
6 activity_task: [android.app.IActivityTaskManager]
7 adb: [android.debug.IAdbManager]
8 alarm: [android.app.IAlarmManager]
...
267 vold: [android.os.IVold]
268 vpn_management: [android.net.IVpnManager]
269 wallpaper: [android.app.IWallpaperManager]
270 wallpaper_effects_generation: [android.app.wallpapereffectsgeneration.IWallpaperEffectsGenerati
271 webviewupdate: [android.webkit.IWebViewUpdateService]
272 wifi: [android.net.wifi.IWifiManager]
273 wifinl80211: [android.net.wifi.nl80211.IWificond]
274 wifip2p: [android.net.wifi.p2p.IWifiP2pManager]
275 wifiscanner: [android.net.wifi.IWifiScanner]
276 window: [android.view.IWindowManager]
VANTAGENS E DESVANTAGENS
Principais vantagens da arquitetura de serviços do framework:
Abstrair o acesso a determinado recurso do sistema (encapsulamento).

Controlar e centralizar o acesso ao recurso (gerenciamento de concorrência).

Garantir segurança no acesso ao recurso (checagem de permissão).

É claro que esta arquitetura tem seus custos:


Consumo maior de recursos de hardware (CPU, memória, etc).

Overhead de comunicação entre os componentes do sistema operacional.


ARQUITETURA DE SERVIÇOS
SERVIÇOS E PROCESSOS
Não existe uma relação direta entre processos em execução e serviços do framework.

Muitos serviços fazem parte do System Server, executado pelo Zygote no boot do
sistema operacional.

Alguns serviços estão encapsulados em processos nativos, executados pelo init no


boot do Android (Surface Flinger, DRM Server, Media Server, Audio Server, Camera
Server, Gatekeeper, etc).

Os serviços podem também estar encapsulados em aplicações Android (NFC, Phone,


Bluetooth, etc).
PROCESSOS QUE CONTÉM SERVIÇOS
system_server: processo que contém muitos serviços do framework (sensor, power,
activity, package, location, etc).

mediaserver: serviço de media player do Android, responsável pela codificação e


decodificação de mídia.

audioserver: serviço de áudio, responsável por interagir com a camada de som do


sistema operacional.

cameraserver: serviço de câmera.


SERVIÇOS E PROCESSOS (CONT.)
surfaceflinger: serviço responsável pela composição da interface gráfica no display.

drmserver: serviço responsável pelo gerenciamento de conteúdo protegido por


direitos autorais (Digital Rights Management).

gatekeeperd: serviço responsável pela autenticação do usuário (padrão, pin, senha)


em um ambiente de execução seguro (TEE - Trusted Execution Environment).

com.android.bluetooth: serviço de Bluetooth, provido pela aplicação de Bluetooth.


INICIALIZAÇÃO DOS SERVIÇOS
SERVIÇOS EM EXECUÇÃO
# ps -A
USER PID PPID VSZ RSS WCHAN ADDR S NAME
root 1 0 10982076 9096 do_epoll_+ 0 S init
system 240 1 10772980 4368 do_epoll_+ 0 S servicemanager
root 405 1 14358672 157028 do_sys_po+ 0 S zygote
audioserver 453 1 11045844 19236 binder_wa+ 0 S audioserver
system 456 1 10929652 23612 do_epoll_+ 0 S surfaceflinger
drm 489 1 34488 6500 binder_wa+ 0 S drmserver
cameraserver 492 1 11084840 18608 binder_wa+ 0 S cameraserver
media 498 1 96592 22900 binder_wa+ 0 S mediaserver
system 523 1 10853128 4812 binder_wa+ 0 S gatekeeperd
system 648 405 15350784 313120 do_epoll_+ 0 S system_server
u0_a78 1238 405 13983844 218616 do_epoll_+ 0 S com.android.systemui
bluetooth 1406 405 13963444 127520 do_epoll_+ 0 S com.android.bluetooth
secure_elem+ 1426 405 13786920 92552 do_epoll_+ 0 S com.android.se
radio 1439 405 13878448 143984 do_epoll_+ 0 S com.android.phone
system 1453 405 13865004 139252 do_freeze+ 0 S com.android.settings
u0_a74 1539 405 13946280 207184 do_epoll_+ 0 S com.android.launcher3
u0_a84 1720 405 13806908 103640 do_epoll_+ 0 S android.ext.services
u0_a63 1745 405 13794448 106320 do_freeze+ 0 S com.android.deskclock
...
COMUNICAÇÃO COM OS SERVIÇOS
A comunicação com os serviços do framework é realizada através do Binder.

Na inicialização do Android, o Service Manager (servicemanager) se registra no


Binder (/dev/binder) como o gerenciador de contexto da comunicação.
Durante o restante da inicialização, os serviços se registram no Service Manager.

Para se comunicar com um serviço do sistema, uma aplicação Android requisita para
o Service Manager uma referência (handle) para o serviço.
Com a referência, a aplicação pode invocar os métodos do serviço.

Este mecanismo de comunicação é totalmente transparente para o desenvolvedor de


aplicações, que precisa conhecer apenas as APIs do Android.
EXEMPLO: POWER MANAGER SERVICE
1 // get handle to the Power Manager Service
2 PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE);
3
4 // create a wakelock object
5 PowerManager.WakeLock wakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK, "myWakeLock");
6
7 // request a wakelock (this will generate a RPC to the service)
8 wakeLock.acquire();
9
10 // do the work here
11 ...
12 ...
13 ...
14
15 // release the wakelock (this will generate a RPC to the service)
16 wakeLock.release();
DIAGRAMA: POWER MANAGER SERVICE
DOCUMENTAÇÃO DOS SERVIÇOS
Não existe uma documentação formal dos serviços do framework, então para
entender seu funcionamento pode ser necessário ler muito código-fonte!

Por exemplo, o ponto de entrada do System Server está disponível em:


frameworks/base/services/java/com/android/server/SystemServer.java

Pode ser um começo para se aventurar nas milhares de linhas de código de cada um
dos serviços do framework!
APLICAÇÕES INICIADAS POR SERVIÇOS
Os serviços do framework são responsáveis por iniciar as aplicações Android
automaticamente no boot do sistema:
O Activity Manager Service inicia automaticamente as aplicações que possuem o
atributo android:persistent="true".

O Activity Manager Service envia um Intent do tipo BOOT_COMPLETED,


notificando sobre o final do processo de boot para sistema.

O Input Method Manager Service inicia todas as aplicações que possuem o filtro
de Intent android.view.InputMethod.

O Activity Manager Service envia um Intent do tipo CATEGORY_HOME para


executar a aplicação registrada como Launcher no sistema.
FERRAMENTAS DE LINHA DE COMANDO
am: permite se comunicar diretamente com o Activity Manager Service.

pm: permite se comunicar diretamente com o Package Manager Service.

sm: permite se comunicar diretamente com o Storage Manager Service.

wm: permite interagir com o Window Manager Service.

ime: permite se comunicar diretamente com o Input Method Manager Service.

input: permite injetar eventos de entrada no sistema (teclado, touchscreen, etc).


FERRAMENTAS DE LINHA DE COMANDO (CONT.)
svc: permite se comunicar com vários outros serviços (Power, USB, NFC, etc).

cmd: ferramenta que interage com um serviço do framework via sua interface de
comandos (exemplo implementado pelo serviço media_session).

service: implementa uma interface genérica que permite interagir com qualquer
serviço do framework.

dumpsys: capaz de extrair informações de debug de um serviço do framework, caso


o serviço tenha implementado o método dump().
MODIFICANDO O FRAMEWORK
O framework do Android é bastante monolítico e customizá-lo (ex: remover ou
desabilitar um serviço) pode exigir alteração no código-fonte.

Alterar o código-fonte do framework pode trazer alguns desafios, incluindo:


Possibilidade de quebrar a API ou causar uma regressão.

Dificuldade de certificação (pode não passar pelo CTS).

Trabalho de manter e portar as alterações para outras versões do Android.

Se possível, evite alterar o código-fonte do framework.


É possível estender o comportamento do framework (ex: adicionar um novo
serviço ou API) sem alterar diretamente o código-fonte do AOSP.
LABORATÓRIO 9

MANIPULANDO O FRAMEWORK
ANDROID EMBARCADO

ESTENDENDO O FRAMEWORK
ESTENDENDO O FRAMEWORK
Em algumas situações, por exemplo em sistemas embarcados, pode ser necessário
acessar determinado tipo de dispositivo de hardware sem suporte na API do Android
(GPIO, porta serial, E2PROM, etc).

Na ausência de uma API, a arquitetura e o modelo de segurança do Android não


permitem que uma aplicação acesse diretamente um dispositivo de hardware.

Neste caso, a melhor solução é estender o framework do Android para adicionar o


suporte a um novo dispositivo de hardware.
SERVIÇO E API
A APLICAÇÃO
A idéia é desenvolver uma aplicação Android que possa utilizar uma API simples para
acessar a porta serial.
SerialPortManager sp = SerialPortManager.getInstance();
sp.tx(ch);

Queremos também controlar a permissão de acesso à porta serial. Para que a


aplicação possa acessar o serviço, precisará requisitar permissão no arquivo de
manifesto:
<uses-permission
android:name="vendor.labworks.serialportservice.permission.SERIALPORT">
</uses-permission>
IMPLEMENTANDO O SERVIÇO
A implementação de um serviço do framework pode ser dividida em quatro etapas:
Definir a interface do serviço (AIDL).

Implementar o serviço (Java).

Implementar o código nativo do serviço para se comunicar com a HAL (C++).

Implementar a classe Manager (API).


1. DEFININDO A INTERFACE DO SERVIÇO
A interface de um serviço do framework pode ser definida através da AIDL.
interface ISerialPortService {
void tx(byte data);
byte rx();
}

Uma ferramenta chamada aidl será responsável por converter o arquivo .aidl em
duas classes Java:
Proxy: utilizada pelo cliente (API) para enviar as requisições.

Stub: utilizada pelo serviço para receber as requisições.

Mais informações sobre AIDL na documentação do Google:


https://developer.android.com/guide/components/aidl.html
CLASSES PROXY E STUB
2. IMPLEMENTANDO O SERVIÇO
Para implementar o serviço, basta extender a classe Stub da interface e implementar
seus métodos.

O serviço será responsável também por realizar as verificações de permissão de


acesso.

Para registrar e executar o serviço, existem algumas possibilidades:


Adicionar o serviço no código-fonte do SystemServer (não recomendado).

Criar uma aplicação separada (abordagem adotada no treinamento).


CÓDIGO-FONTE DO SERVIÇO
1 // SerialPortServiceImpl.java
2
3 class SerialPortServiceImpl extends ISerialPortService.Stub {
4 public static final String SERIALPORT_PERM =
5 "vendor.labworks.serialportservice.permission.SERIALPORT";
6 private final Context mContext;
7
8 public SerialPortServiceImpl(Context context) {
9 mContext = context;
10 }
11
12 public void tx(byte data) {
13 mContext.enforceCallingOrSelfPermission(SERIALPORT_PERM, null);
14 nativeTx(data);
15 }
16
17 public byte rx() {
18 mContext.enforceCallingOrSelfPermission(SERIALPORT_PERM, null);
19 return nativeRx();
20 }
21
22 private static native void nativeTx(byte data);
23 private static native byte nativeRx();
24 }
CÓDIGO-FONTE DA APLICAÇÃO
1 // SerialPortService.java
2
3 package vendor.labworks.serialportservice;
4
5 import android.app.Application;
6 import android.os.ServiceManager;
7 import android.util.Log;
8
9 public class SerialPortService extends Application {
10 private static final String SERVICE_NAME = "serialport";
11 private SerialPortServiceImpl serviceImpl;
12
13 public void onCreate() {
14 super.onCreate();
15 Log.i("SerialPortService", "Starting service...");
16 serviceImpl = new SerialPortServiceImpl(getApplicationContext());
17 ServiceManager.addService(SERVICE_NAME, serviceImpl);
18 }
19
20 public void onTerminate() {
21 super.onTerminate();
22 }
23 }
3. IMPLEMENTANDO O CÓDIGO NATIVO COM JNI
Serviços que gerenciam o acesso a um dispositivo de hardware utilizam
normalmente JNI para se comunicar com o hardware via HAL.
$ ls frameworks/base/services/core/jni

JNI (Java Native Interface) é um framework de desenvolvimento que possibilita um


código Java rodando em uma máquina virtual acessar um código nativo rodando
fora da VM (C, C++, etc).
https://en.wikipedia.org/wiki/Java_Native_Interface

Algumas dicas sobre como trabalhar com JNI no Android:


https://developer.android.com/training/articles/perf-jni
CÓDIGO-FONTE NATIVO DO SERVIÇO
1 static std::shared_ptr<ISerialPort> serialport = nullptr;
2
3 JNIEXPORT void JNICALL
4 Java_vendor_labworks_serialportservice_SerialPortServiceImpl_nativeTx
5 (JNIEnv *, jclass, jbyte data) {
6 if (serialport == nullptr) {
7 const std::string instance = std::string() + ISerialPort::descriptor + "/default";
8 serialport = ISerialPort::fromBinder(ndk::SpAIBinder(AServiceManager_checkService(instanc
9 }
10 serialport->tx(data);
11 }
12
13 JNIEXPORT jbyte JNICALL
14 Java_vendor_labworks_serialportservice_SerialPortServiceImpl_nativeRx
15 (JNIEnv *, jclass) {
16 if (serialport == nullptr) {
17 const std::string instance = std::string() + ISerialPort::descriptor + "/default";
18 serialport = ISerialPort::fromBinder(ndk::SpAIBinder(AServiceManager_checkService(instanc
19 }
20 int8_t data = 0;
21 serialport->rx(&data);
22 return data;
23 }
IMPLEMENTANDO A API (CLASSE MANAGER)
As classes Manager são uma abstração para acessar os serviços do framework
(Facade Design Pattern).

Estas classes implementam a API para o desenvolvedor de aplicações consumir a


interface do serviço sem se preocupar com os detalhes da comunicação via Binder
(LocationManager, SmsManager, SensorManager, etc).

Para implementar a classe Manager, basta invocar os métodos do serviço utilizando a


classe Proxy gerada pelo AIDL.

A classe Manager pode ser implementada e distribuída em uma biblioteca Java


separada via Platform Library ou Java SDK Library.
device/sample/frameworks/PlatformLibrary/README.txt
https://source.android.com/devices/architecture/java-library
CÓDIGO-FONTE DA CLASSE MANAGER
1 // SerialPortManager.java
2
3 public class SerialPortManager {
4 private static ISerialPortService service;
5 ...
6 private SerialPortManager() {
7 IBinder binder = ServiceManager.getService(SERVICE_NAME);
8 service = ISerialPortService.Stub.asInterface(binder);
9 ...
10 }
11 public static SerialPortManager getInstance() {
12 if(instance == null) {
13 instance = new SerialPortManager();
14 }
15 return instance;
16 }
17 public void tx(byte data) throws RemoteException {
18 service.tx(data);
19 }
20 public byte rx() throws RemoteException {
21 return service.rx();
22 }
23 }
IMPLEMENTAÇÃO FINAL
LABORATÓRIO 10

IMPLEMENTAÇÃO DE SERVIÇOS DO FRAMEWORK


ANDROID EMBARCADO

APLICAÇÕES ANDROID
APLICAÇÕES ANDROID
APLICAÇÕES ANDROID
Tudo o que vimos até aqui tem como principal objetivo prover uma infraestrutura
completa para o desenvolvimento de aplicações Android.

As aplicações Android são escritas Java ou Kotlin, compiladas em um formato


chamado DEX (Dalvik Executable) e executadas na máquina virtual ART.
Também podem ser escritas em código nativo (C++) através do NDK.

São empacotadas em arquivos com extensão .apk ou .aab (Android App Bundle).

Podem ser instaladas através do Google Play ou manualmente (side-loaded) via ADB
ou qualquer gerenciador de arquivos.
ANDROID API
Todo o desenvolvimento de aplicações Android é baseado na API provida pelo
Google.

Esta API contém dois grandes conjuntos de classes:


Classes Java padrão (java.*, javax.*, etc).

Classes específicas do Android (android.*, dalvik.*).

Toda a documentação da API do Android está disponível no site do projeto.


https://developer.android.com/reference/packages

Diversos serviços, frameworks e bibliotecas estão disponíveis para auxiliar e


aumentar a produtividade do desenvolvedor (Android Jetpack, Firebase, Flutter,
etc).
COMPONENTES DE APLICAÇÕES ANDROID
As aplicações Android são compostas por uma combinação de 4 tipos de
componentes:
Activities.

Services.

Broadcast receivers.

Content providers.
ACTIVITIES
Activities representam as telas de interface com o usuário (conceito similar às janelas
em sistemas desktop).
https://developer.android.com/guide/components/activities

Uma aplicação Android normalmente possui diversas Activities, e uma das activities
é definida como a principal (main), que é apresentada ao usuário quando a
aplicação é executada pelo Launcher.

Uma aplicação pode requisitar o uso de uma Activity de outra aplicação, se tiver
permissão.
Por exemplo, uma aplicação de navegação web pode requisitar o uso da Activity
da aplicação de envio de e-mail para compartilhar um link por e-mail.
SERVICES
Services (serviços) são componentes que rodam em background e não tem interface
com o usuário.
https://developer.android.com/guide/components/services

São usados para executar operações em background ou prover serviços para outras
aplicações (ex: playback de áudio, sincronização com servidor, etc).

Cuidado para não confundir com os serviços do framework ou com os serviços do


init!
BROADCAST RECEIVERS
Broadcast receivers são componentes que possibilitam receber eventos do sistema
ou de uma aplicação.
https://developer.android.com/guide/components/broadcasts

Possuem o mesmo conceito de uma rotina de tratamento de sinal ou interrupção


(publish-subscribe design pattern).

Exemplos de eventos de broadcast:


Alarme expirou.

Tela foi desligada.

O modo avião foi habilitado.

O processo de boot do sistema completou.


CONTENT PROVIDERS
Content providers são componentes que possibilitam abstrair e controlar o acesso a
dados por aplicações.
https://developer.android.com/guide/topics/providers/content-providers

Uma aplicação pode utilizar um content provider quando deseja abstrair o


armazenamento de informações e compartilhar dados com outras aplicações.
Por exemplo, informações sobre os contatos do usuário ficam armazenadas em
um content provider, e qualquer aplicação (que tenha permissão de acesso) pode
acessar estes dados.

Ao criar um content provider, o desenvolvedor escolhe como deseja armazenar os


dados, seja localmente (SQlite, XML, texto, etc) ou em um servidor remoto.
ARQUIVO DE MANIFESTO
Toda aplicação possui um arquivo de manifesto (AndroidManifest.xml), cujo objetivo
é declarar informações sobre a aplicação, dentre elas:
Componentes disponíveis (activities, services, broadcasts receivers, etc).

Permissões necessárias para executar a aplicação.

Nível mínimo da API que o aplicativo exige.

Características de hardware (display, câmera, etc).

Documentação completa no site do projeto.


https://developer.android.com/guide/topics/manifest/manifest-intro
EXEMPLO DE ARQUIVO DE MANIFESTO
<manifest package="com.example.android.notepad">
<application android:icon="@drawable/app_notes"
android:label="@string/app_name" >
<activity android:name="NotesList"
android:label="@string/title_notes_list">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.EDIT" />
<action android:name="android.intent.action.PICK" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="vnd.android.cursor.dir/vnd.google.note" />
</intent-filter>
</activity>
</application>
</manifest>
COMUNICAÇÃO ENTRE COMPONENTES
Uma aplicação pode conter um ou mais componentes.

Uma aplicação pode iniciar qualquer componente, incluindo componentes de outras


aplicações.

Os componentes comunicam-se entre si através de um mecanismo de troca de


mensagens chamado intent.
INTENT
O intent é uma abstração que serve de mecanismo de comunicação entre os
componentes de uma aplicação Android.
https://developer.android.com/reference/android/content/Intent

Um intent descreve basicamente a intenção de realizar alguma ação (iniciar serviço,


abrir activity, enviar broadcast, etc).
Por exemplo, a activity de uma aplicação pode enviar um intent para ver um
arquivo PDF.

O intent contém duas principais informações:


action: operação que deseja-se realizar (ver, editar, etc).

data: dados associados à operação no formato URI (Uniform Resource Identifier).


INICIANDO E RESOLVENDO INTENTS
Activities, Services e Broadcast receivers são iniciados através de intents.

Os componentes declaram quais intents são capazes de manipular através do


atributo intent-filter no arquivo de manifesto da aplicação.

Internamente, os intents são implementados via Binder e resolvidos pelo Activity


Manager Service.

Mais informações sobre como trabalhar com intents no site do projeto.


https://developer.android.com/guide/components/intents-filters.html
EXEMPLO DE USO DE INTENT
Uma aplicação pode registrar uma activity como navegador web com o seguinte
código no arquivo de manifesto:
<!-- AndroidManifest.xml -->
<activity android:name=".BrowserActivitiy"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="https"/>
</intent-filter>
</activity>

O seguinte código pode ser utilizado para iniciar esta activity:


Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse("https://sergioprado.blog"));
startActivity(i);
RESOLUÇÃO DE INTENTS
PROCESSOS NO ANDROID
Por padrão, todos os componentes de uma aplicação Android rodam no mesmo
processo.

Quando for necessário iniciar um novo componente:


Se o processo correspondente ao componente ainda não estiver em execução,
um novo processo será criado.

Caso contrário, o componente será iniciado dentro do processo em execução.

É possível mudar este comportamento e iniciar um componente em um novo


processo declarando o atributo android:process no arquivo de manifesto da
aplicação.
https://developer.android.com/guide/topics/manifest/activity-element#proc
THREADS DE EXECUÇÃO
Uma aplicação Android possui (por padrão) apenas uma thread de execução, então
todos os componentes e interações com o usuário são realizadas de forma
sequencial.

Por este motivo, uma operação de I/O ou processamento mais longo na thread
principal pode travar a aplicação!

Se a interface da aplicação bloquear por um tempo (5 segundos por padrão), o


sistema irá exibir a mensagem "Application Not Responding" (ANR).

Por este motivo, é aconselhável executar operações mais longas em uma thread
separada.
THREADS NO ANDROID
Nas versões mais antigas da API do Android, AsyncTask era a classe recomendada
para operações em background, principalmente as que exigiam interações com a
interface gráfica.
Esta API foi descontinuada nas versões mais recentes do Android.

Atualmente, existem algumas opções para processamento em background, incluindo


a classe WorkManager, a classe Java padrão java.util.concurrent e coroutines (para
código em Kotlin).

O site do projeto possui uma documentação bem completa sobre o processamento


de tarefas em background no Android.
https://developer.android.com/guide/background
GERENCIAMENTO DE PROCESSOS EM EXECUÇÃO
Um ponto central do Android é que o usuário não precisa se preocupar com o
gerenciamento das aplicações e processos em execução.

O usuário pode iniciar quantas aplicações quiser e chavear entre elas conforme a
necessidade.
É o sistema operacional que gerencia o ciclo de vida das aplicações!

Mas após um certo tempo, vários processos podem estar em execução, aumentando
o consumo de recursos (CPU, memória, etc) do sistema.
CICLO DE VIDA DA APLICAÇÃO
Por este motivo, o Android gerencia o ciclo de vida de cada um dos componentes da
aplicação, matando processos e liberando recursos conforme a necessidade.

Para este processo funcionar corretamente, o desenvolvedor deve implementar uma


série de callbacks para cada componente da aplicação.

Estas callbacks são chamadas de acordo com o evento do ciclo de vida do


componente da aplicação.

Por exemplo, na mudança de foreground para background, a callback onPause() de


uma Activity é chamada.
CICLO DE VIDA DE UMA ACTIVITY
GERENCIAMENTO DE MEMÓRIA
Quando o uso de memória estiver alto, o sistema pode decidir matar um processo
para liberar memória (Low Memory Killer).

Esta decisão é tomada baseada em diversos fatores, incluindo a importância


(prioridade) do processo.

Quando um processo é encerrado, todos os seus componentes também são.

Ao encerrar um processo, o framework define a prioridade de cada processo de


acordo com seu status, de processos sem componentes ou com componentes
inativos até o processo rodando em foreground (ex: activity em execução).
SEGURANÇA EM APLICAÇÕES
As aplicações Android rodam em uma Sandbox, isoladas de outras aplicações e
seguindo o princípio do menor privilégio:
Cada aplicação roda em um processo separado.

Cada aplicação roda em uma instância da máquina virtual.

O sistema atribui um usuário (UID) e grupo (GID) para cada aplicação (estas
credenciais garantem que as aplicações tenham acesso apenas a um limitado
conjunto de recursos do sistema operacional).

As aplicações Android são assinadas para garantir autenticidade.


PERMISSÕES EM APLICAÇÕES
Para que uma aplicação possa acessar um recurso que não tenha permissão, o
desenvolvedor deve declarar as permissões necessárias no arquivo de manifesto da
aplicação.
<uses-permission android:name="android.permission.WRITE_SMS" />
<uses-permission android:name="android.permission.READ_SMS" />
<uses-permission android:name="android.permission.RECEIVE_SMS" />

Durante a instalação ou execução da aplicação (dependendo do tipo de permissão),


o usuário precisará aprovar o acesso ao recurso.

Uma lista completa das permissões existentes está disponível no site do projeto.
https://developer.android.com/reference/android/Manifest.permission
WORKFLOW PARA TRABALHAR COM PERMISSÕES
VERIFICANDO PERMISSÕES
NDK
O NDK (Native Development Kit) é um conjunto de ferramentas para o
desenvolvimento de aplicações Android em C/C++.

Seu uso é comum em algumas situações, incluindo:


Performance (ex: cálculos matemáticos, jogos, etc).

Reuso de código (ex: Firefox, snes9x, etc).

Uso de chamadas de sistema específicas do kernel (ioctl(), etc).

Permite acesso direto à GPU via OpenGL/Vulkan!

Pode ser combinado com o SDK, permitindo ter uma aplicação parte em Java e parte
em C/C++.
NDK (CONT.)
Aplicações desenvolvidas com NDK também são empacotadas em um .apk.

Apesar do código não ser executado pela máquina virtual, todos os mecanismos de
segurança são mantidos.

Algumas deficiências:
Perde portabilidade.

Acesso apenas a uma parte da API do Android.

Não possui a funcionalidade de recursos.


DISTRIBUINDO APLICAÇÕES
As aplicações Android podem ser instaladas manualmente, mas normalmente são
distribuídas para os usuários através de um repositório de aplicações.

O repositório padrão do Android é o Google Play.

Existem outros repositórios disponíveis como o Amazon AppStore.

O projeto F-Droid possibilita criar um repositório de aplicações customizado:


https://www.f-droid.org/
https://gitlab.com/fdroid
APLICAÇÕES DEDICADAS NO ANDROID
Desenvolver um produto com Android embarcado significa utilizar o sistema
operacional Android para desenvolver um dispositivo com um objetivo ou propósito
específico.

Neste caso, queremos nos beneficiar de todos os recursos do sistema operacional,


porém esconder ou desabilitar todos os elementos da sua interface gráfica (barras de
status, barras de navegação, notificações, botões de navegação, outras aplicações,
etc).

No Android, existem alguns recursos que podemos utilizar para executar uma
aplicação de forma dedicada (kiosk mode):
https://developer.android.com/work/dpc/dedicated-devices
DESENVOLVENDO APLICAÇÕES DEDICADAS
Passo 1: desenvolver uma aplicação do tipo Launcher.

Passo 2: executar a aplicação de forma dedicada (modo imersivo), com os elementos


da interface gráfica escondidos.

Passo 3: Travar a aplicação para ser a única em execução com o recurso lock task
mode.
PASSO 1: LAUNCHER
O primeiro passo para desenvolver uma aplicação dedicada no Android é
transformá-la em um Launcher.

A aplicação Launcher é a aplicação executada automaticamente no boot do Android.

Transformar a aplicação em um Launcher é simples, bastando declarar os seguintes


intents na Activity principal da aplicação:
<category android:name="android.intent.category.HOME" />
<category android:name="android.intent.category.DEFAULT" />
PASSO 2: MODO IMERSIVO
O próximo passo para executar a aplicação de forma dedicada é rodar em modo
imersivo, escondendo os elementos de interface gráfica do Android.

Para isso, podemos utilizar algumas APIs do Android, incluindo:


Desabilitar a barra de título com o método getSupportActionBar() da activity.

Desabilitar elementos da interface gráfica com a classe WindowInsetsController.

Manipular flags adicionais da UI com o método getWindow() da activity.


PASSO 2: MODO IMERSIVO (CONT.)
1 protected void onCreate(Bundle savedInstanceState) {
2 super.onCreate(savedInstanceState);
3 setContentView(R.layout.activity_main);
4 new SerialPortRx().execute();
5
6 // hide title bar
7 getSupportActionBar().hide();
8
9 // immersive mode
10 getWindow().setDecorFitsSystemWindows(false);
11 WindowInsetsController controller = getWindow().getInsetsController();
12 if (controller != null) {
13 controller.hide(WindowInsets.Type.statusBars() | WindowInsets.Type.navigationBars() | Win
14 controller.setSystemBarsBehavior(WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_S
15 }
16
17 // display always ON
18 getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
19 }
PASSO 3: LOCK TASK MODE
A funcionalidade de lock task mode permite travar o Android em uma aplicação
específica.
https://developer.android.com/work/dpc/dedicated-devices/lock-task-mode

Neste modo, é possível executar a aplicação de forma totalmente dedicada e


imersiva:
Apenas a aplicação selecionada será executada.

As notificações estarão desabilitadas.

Os botões (volume, navegação, etc) podem ser desabilitados.

Para utilizar este modo, é necessário fazer algumas alterações no código da


aplicação, além de registrá-la como administradora do dispositivo através da
ferramenta de linha de comando dmp (Device Policy Control).
LABORATÓRIO 11

DESENVOLVIMENTO DE APLICAÇÕES ANDROID


ANDROID EMBARCADO

RECURSOS ADICIONAIS
LINKS
Site oficial do sistema operacional Android:
https://www.android.com/

Documentação para o desenvolvedor de plataforma (AOSP):


https://source.android.com/

Documentação para o desenvolvedor de aplicações:


https://developer.android.com/

Android Developers Blog:


https://android-developers.googleblog.com/

Android Developers Backstage (podcast):


https://adbackstage.libsyn.com/
LINKS (CONT.)
Seção sobre Android da Embedded Linux Wiki (um pouco desatualizada):
http://www.elinux.org/Android_Portal

emteria Blog:
https://emteria.com/blog

Android Bytes (blog e podcast), mantido pela Esper.io:


https://blog.esper.io/android-bytes-podcast/

Android microconference (Linux Plumbers Conference):


https://lpc.events/event/16/sessions/134/#20220913

The AOSP and AAOS Meetup:


https://aospandaaos.github.io/
LIVROS
Embedded Android - Karim Yaghmour

Android Internals (Volume 1 e Volume 2) - Jonathan Levin

Android Security Internals - Nikolay Elenkov

Inside the Android OS - G. Blake Meike, Larry Schiefer, Lawrence Schiefer

Androids: The Team That Built the Android Operating System - Chet Haase
ANDROID EMBARCADO

OBRIGADO!

Você também pode gostar