Escolar Documentos
Profissional Documentos
Cultura Documentos
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
Troque experiências...
Ajude...
Participe!
INSTRUÇÕES PARA AULAS ONLINE
Não é permitido gravar o treinamento, mesmo para uso pessoal.
Interaja e participe!
ANDROID EMBARCADO
Equipamentos de TV à cabo.
Terminais de auto-atendimento.
Relógios inteligentes.
Tecnologias de comunicação sem fio (Bluetooth, WiFi, NFC, GSM, CDMA, UMTS,
LTE, etc).
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.
Android Tablet.
Android Automotive.
Android TV.
Android Wear.
CÓDIGO-FONTE
AOSP E REPOSITÓRIOS GIT
O AOSP é versionado pelo Google através do Git.
<remote name="aosp"
fetch=".."
review="https://android-review.googlesource.com/" />
<default revision="refs/tags/android-13.0.0_r44"
remote="aosp"
sync-j="4" />
$ 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
Através das ferramentas git e repo, qualquer pessoa pode contribuir com o
desenvolvimento do Android:
$ 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.
external/: projetos externos utilizados pelo Android (libusb, sqlite, mksh, etc).
DIRETÓRIOS: FRAMEWORK E APLICAÇÕES
dalvik/: máquina virtual Dalvik.
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
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.
Yocto Project/OpenEmbedded.
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).
Soong e ninja são substituídos pelo Bazel para a execução do processo de build.
Diversos pré-requisitos de software: git, python 3, make, unzip, curl, libncurses, etc.
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.
croot: volta para o diretório raiz do AOSP (ou subdiretório a partir da raiz).
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
Executando o emulador:
$ emulator
Executando o emulador:
$ launch_cvd
Smartphone/tablet: 16G+.
OUTRAS CARACTERÍSTICAS COMUNS
Display com touchscreen.
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.
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.
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.
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/
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.
# 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.
É 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.
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.
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.
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).
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.
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.
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.
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).
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).
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).
Diversas bibliotecas C estão disponíveis para sistemas Linux, incluindo glibc, uclibc-
ng e musl.
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
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.
É bem mais limitado que o shell disponível em uma distribuição Linux desktop (ex:
Bash).
Funcionalidades do ueventd:
Carregar o firmware de dispositivos de hardware.
Configurar tethering.
Para ler ou escrever nos logs, não é necessário acessar diretamente estes sockets.
Para isso, as aplicações podem utilizar a biblioteca liblog.
É invocado pelo linker da Bionic para fazer análise postmortem de um processo que
finalizou a execução de forma inesperada.
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.
# ls -l /init
lrwxr-x--- 1 root shell 16 2023-05-04 18:59 /init -> /system/bin/init
# 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
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.
init
late-init
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
on init
sysclktz 0
...
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.
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.
/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
# 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
# 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).
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.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.
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.
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.
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)
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).
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 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.
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.
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
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.
Por este motivo, se possível escolha um dispositivo de hardware que já possua uma
implementação de HAL do Android.
$ 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.
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).
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.
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();
}
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
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).
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/.
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).
Muitos serviços fazem parte do System Server, executado pelo Zygote no boot do
sistema operacional.
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.
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 Input Method Manager Service inicia todas as aplicações que possuem o filtro
de Intent android.view.InputMethod.
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.
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).
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.
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.
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.
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).
Por este motivo, uma operação de I/O ou processamento mais longo na thread
principal pode travar a aplicação!
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.
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.
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).
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++.
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.
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 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.
RECURSOS ADICIONAIS
LINKS
Site oficial do sistema operacional Android:
https://www.android.com/
emteria Blog:
https://emteria.com/blog
Androids: The Team That Built the Android Operating System - Chet Haase
ANDROID EMBARCADO
OBRIGADO!