[1] 历史
2003年Android公司成立,系统开发2005年Android被google收购
2007年11月5日,google公司推动成立手机开发联盟(HAL)
2008年9月Android 1.0正式发布,HTC G1使用
[2] 如何研究学习系统?
1. 了解系统
(1) 架构
(2) 内核
(3) 文件系统
操作系统 = 内核 + 文件系统(数据和程序分类存储)
2. 使用系统
运行
3. 研究学习系统原理
(1) 获取源码
(2) 编译运行
(3) 编译原理
(4) 启动过程
(5) 定制系统
[3] 概述
理想的手机操作系统 = 应用通用 + 容易开发友好的应用 + 容易开发手机应用
应用层
---------------------------------------
如何让手机应用容易开发?
如何让友好的应用容易?
应用框架层
---------------------------------------
如何让应用通用?
vm
系统运行库 和 系统服务
---------------------------------------
如何让硬件更容易使用?
linux内核
---------------------------------------
hardware
[4] 应用层
1. Android 系统中所有应用程序的地位平等, 系统不绑定--开放
2. 应用层是应用程序开发工程师工作的层次
[5] 应用框架层(Application Framework)
如何让友好的应用容易?
1. Activity Manager
管理Activity之间的切换
2. Window Manager
管理窗口之间的切换
Activity = Window + 用户交互代码
3. Content Provider
程序<--------------Content Provider------------>程序
例:
phone 联系人
sms
4. View System
基本界面组件实现
5. Package Manager
管理应用程序包
6. Resource Manager
资源管理, 资源包含: 字符串、图片和布局
7. XMPP(Extensible Messging and Presence Protocol) Service
可扩展消息与表示协议, 四大即时通讯协议协议,基于xml脚本实现。
Google的Gtalk基于基于协议协议。
如何让手机应用容易开发?
1. Notification Manager
通知管理
2. telephone Manager
电话管理
3. Location Manager
定位管理,可以获得当前的位置信息
注意: 蓝颜色的部分用java语言实现
[6] 虚拟机
1. Core Libaries
Java语言核心库
2. Dalvik(冰岛小渔村的名字, 非常小,非美丽)Virtual Machine
(1) 每个应用程序运行在自己独立的虚拟机上, 每个虚拟机一个进程
(2) 基于寄存器(指令支持的操作数只能是寄存器和立即数)实现
(3) 执行.dex文件(针对内存做了优化)
(4) java类--->.class文件---dx(SDK)---->.dex文件
(5) 依赖于2.6以上版本的内核, 因为在2.6以上版本的内核中,加入一些虚拟机需要的机制:
线程和底层的内存管理机制
[7] 系统运行库
1. libc
标准C库
2. SSL(Secure Socket Layer 安全套接层)
在网络传输时, 加入对数据的加密, 有以下三个功能:
(1) 使用公钥证书对双端进行认证
(2) 通讯加密
(3) 数据完整性检查
3. SGL
2D图形加速引擎
4. Webkit
web浏览器引擎,支持了Android和一个内嵌web视图
5. FreeType
位图和适量字体图库(字库)
6. OpenGL | ES
3D图形加速引擎
7. SQLite
开源小型关系型数据库
8. Media Framework
基于PacketVideo Open Core实现, 支持很多的多媒体格式,音频(mp3, AAC和AMR)、视频(mpeg4、H.264 ...)
支持图片文件(jpg、png...)
9. Surface Manager
对显示子系统进行管理,用于应用程序的2D和3D图形融合
注意: 绿颜色部分用C/C++实现
[8] kernel
Binder 用于android系统中进程间通信
[9] 文件系统
1. /d
链接到/sys/kernel/debug
2. /data
用户数据
3. /dev
设备文件
4. /etc
链接到/system/etc
5. /mnt
外部文件系统的挂载点, 如: SDcard
6. /proc 和 /sys
挂载procfs和sysfs虚拟文件系统
7. /root
root用户的home目录
8. /sbin
基本调试工具和启动程序
9. /sdcard
链接到/mnt/sdcard目录
10. /system
* /system/app apk应用程序
* /system/preinstall
/system/pri-app
* /system/bin linux应用程序(常用工具)
* /system/etc 启动脚本和配置文件
* /system/fonts 字库
* /system/framework Application Framework编译出来的包
* /system/lib linux的动态库
/system/vendor/lib 厂家动态库
/system/media 开机音乐和系统logo
* /system/vendor/modules linux驱动模块
/system/xbin linux应用程序
/vendor
链接到/system/vendor
[10] 获取源码及编译环境配置
1. Android官网
(1) 下载源码(repo 和 git)
断电续传的脚本:
#!/bin/sh
repo sync
while [ $? -ne 0 ]
do
repo sync
done
(2) 配置编译环境
《开源平板编译环境配置.docx》
2. 芯片厂家(芯片代理商/开发板厂家)
3. SOC开源社区
[10] Android源代码目录
dalvik 虚拟机相关工具
*device(vendor) 厂家目录
ndk 开发本地工具箱代码
*system 系统核心程序和本地服务程序的源码
system/core 系统核心程序源码
system/media 多媒体应用程序支持的源代码
*build 编译脚本(Makefile shell(bash))
*development 开发工具和例子程序源码
*developers 开发工具和例子程序源码
*frameworks 应用框架层源码
*external 第三方的开源库源代码
*hardware 硬件抽象层代码
*packages 应用程序及包的源代码
sdk 应用程序开发工具箱中工具源代码
*out 编译结果
[11] 配置编译
u-boot & kernel
---------------
$ cd lichee
$ ./build.sh config 第一次编译
$ ./build.sh
Android
----------------
$ cd android 4.4
1. source build/envsetup.sh
(1) 功能
1. 添加配置编译命令到当前shell进程
hmm(help) 打印配置编译命令的帮助信息
tapas 配置编译应用程序(full模拟器)
例:
$ tapas packages/apps/Contacts
croot 在任意的android源代码目录下,回到android源代码根目录
m/mm/mmm/mma/mmma 编译命令
cgrep 从C/C++文件中搜索特征字符串
例:
$ cgrep RILD
jgrep 从java文件中搜索特征字符串
resgrep 从资源文件中搜索特征字符串
godir 进入具有指定文件的目录
例:
$ godir rild.c
printenv 打印配置结果
2. 添加"产品型号-编译类型"到选择菜单(shell进程)
产品型号
full android模拟器
vbox_x86 x86的android虚拟机
fspad_723
编译类型
eng 工程机(包含开发工具程序)
userdebug 用户调试机(包含部分调试程序)
user 普通用户机
(2) 结果
命令可以直接shell运行:
$ hmm
2. lunch(配置)
(1) 功能
1. 选择产品型号和编译类型
"产品型号-编译类型"
2. 检查产品是否存在, 存在, 获取产品信息, 根据产品信息判断设备是否存在,存在找到设备,获取设备信息(选择跟硬件相关的软件模块)
3. 检查选择的编译类型(选择软件模块)是否正确
4. 打印选择产品信息及其设备信息
(2) 结果(lunch的结果)
PLATFORM_VERSION_CODENAME=REL 代码名称, REL代表发布版本, 除Android系统开发团队外,其他看到的都是REL
PLATFORM_VERSION=4.4.2 Android版本号
*TARGET_PRODUCT=fspad_723 产品型号
*TARGET_BUILD_VARIANT=eng 编译类型
*TARGET_BUILD_TYPE=release 编译类型(release 和 debug), 可以在Android.mk中使用
TARGET_BUILD_APPS= 指定当前编译的应用模块
空 编译整个Android系统
应用程序(packages) 编译应用程序(full为目标机)
TARGET_ARCH=arm CPU架构
TARGET_ARCH_VARIANT=armv7-a-neon 指令集版本 neon 浮点协处理器
TARGET_CPU_VARIANT=cortex-a7 CPU名
HOST_ARCH=x86 编译Android系统的主机类型
HOST_OS=linux 编译Android系统的操作系统名
HOST_OS_EXTRA=Linux-3.13.0-24-generic-x86_64-with-Ubuntu-14.04-trusty
操作系统名全称
HOST_BUILD_TYPE=release SDK工具为release
BUILD_ID=KVT49L Android版本名
OUT_DIR=out 编译结果的输出目录
2' extract-bsp(平板特有的,非标准文件)
拷贝kernel 和 驱动模块到Android目录下
3. make(编译)
(1) 功能
1. 获取产品信息, 根据产品信息,获取设备信息, 根据设备信息选择源代码模块
2. 根据编译类型,选择产品中需要的所有的软件的源代码
3. 编译源代码
(2) 扩展
*make -j2 启动2(x86 CPU是单核)个线程(job)编译系统
make help 打印帮助信息
*make clean 清除编译出来的所有文件
*make snod 重新生成system.img
*make ramdisk 重新生成ramdisk.img
*make bootimage 重新生成boot.image
make recoveryimage 重新生成recovery.img
make 模块名 编译指定模块
例: $ make rild
*make clean-模块名 清除指定模块
例: $ make clean-rild
m 编译整个android系统,等价于make
*mm 编译当前目录及其子目录下的所有模块,不编译依赖模块
例:
$ cd hardware/ril/rild
$ mm
*mmm 编译指定目录及其子目录下的所有模块, 不编译依赖模块
例:
$ mmm hardware/ril/rild
mma 编译当前目录及其子目录下的所有模块,编译依赖模块
mmma 编译指定目录及其子目录下的所有模块, 编译依赖模块
4. pack
bootloader.fex + boot.img(kernel + ramdisk.img) + system.img + userdata.img + recorvery.img = lichee/tools/pack/sun8iw3p1_android_fspad-723_card0.img
bootloader.fex = BOOT0 + u-boot.bin + logo
[1] source build/envsetup.sh
1. 功能
(1) 添加配置编译命令到shell进程
(2) 添加"产品型号-编译类型"菜单到shell进程
2. 原理
(1) /bin/bash脚本的执行原理
1. 语句
执行
if [ $PATH ]; then
...
fi
2. 命令或脚本
1. fork进程
2. exec(普通程序或脚本, 参数, 环境变量)
echo "hello"
3. 变量赋值
添加"变量=值"到当前shell进程的环境变量区
VAR="hello"
4. 函数定义
添加函数定义到当前shell进程的环境变量区
func()
{
...
}
函数什么执行?
func
3. 总结
(1) 添加配置编译函数到shell进程
(2) 添加"产品型号-编译类型"菜单到shell进程的LUNCH_MENU_CHOICES环境变量数组
$ set | grep LUNCH_MENU_CHOICES
[2] lunch
1. 功能
(1) 选择产品型号和编译类型
"产品型号-编译类型"
(2) 检查产品是否存在, 存在, 获得产品信息, 根据产品信息获得设备信息,
检查设备是否存在, 存在, 获得设备信息
(3) 检查选择的编译类型是否正确, 正确,利用这个信息,选择软件(跟调试有关的)模块
(4) 打印产品信息、设备信息和选择结果(当前shell进程的环境变量区)
注意: 选择结果存放在当前shell进程的环境变量区
2. 原理
(1) 重要的文件(厂家目录结构图.bmp)
vendorsetup.sh 添加"产品型号-编译类型"到lunch菜单
AndroidProducts.mk 产品列表文件
产品名.mk 产品信息文件
BoardConfig.mk 设备信息文件
(2) 选择产品型号和编译类型
通过shell脚本,显示菜单,并且让用选择,选择的结果存放在当前shell进程的环境变量区
(3) 检查产品是否存在, 存在, 获得产品信息, 根据产品信息获得设备信息
检查设备是否存在, 存在, 获得设备信息
1. 获取产品列表文件
find device -maxdepth 6 -name AndroidProducts.mk 当前版本
find vendor -maxdepth 6 -name AndroidProducts.mk 以前版本
build/target/product/AndroidProducts.mk 系统自带的默认产品
2. 获取产品信息文件
在产品列表文件中, PRODUCT_MAKEFILES变量中存放了产品信息文件:
PRODUCT_MAKEFILES := \
$(LOCAL_DIR)/fspad_723.mk \
...
3. 找到选择产品的产品信息文件
用TARGET_PRODUCT中存放的产品名(选择的产品的名称)和整个系统中的产品信息文件中的产品名(存放在PRODUCT_NAME变量)
逐一对比, 如果找到相等的,找到产品信息文件,如果找不到相等的,则产品不存在
4. 获取设备名
产品信息文件中的PRODUCT_DEVICE变量中,就是存放的设备名
5. 获取设备信息文件
PRODUCT_DEVICE----传值---->TARGET_DEVICE
device/*/$(TARGET_DEVICE)/BoardConfig.mk 现在版本的厂家目录
vendor/*/*/$(TARGET_DEVICE)/BoardConfig.mk 以前版本的厂家目录
build/target/product/$(TARGET_DEVICE)/BoardConfig.mk
(4) 检查选择的编译类型是否正确, 正确,利用这个信息,选择软件(跟调试有关的)模块
lunch函数完成
(5) 打印产品信息、设备信息和选择结果(当前shell进程的环境变量区)
printconfig
[3] make(make.pdf)
1. 功能
(1) 根据产品型号, 找到产品信息,获得设备名,根据设备名,找到设备信息
(2) 根据设备信息,选择硬件相关的软件模块
(3) 根据编译类型, 选择调试相关的软件模块
(4) 编译选取的软件模块
2. 原理
(1) 根据产品型号, 找到产品信息,获得设备名,根据设备名,找到设备信息
build/core/confg.mk
(2) 根据设备信息,选择硬件相关的软件模块
根据BoardConfig.mk中的信息,选取软件模块, 如:
如果选取了wifi,wifi依赖的应用程序、库、驱动模块等都会被编译到系统
(3) 根据编译类型, 选择调试相关的软件模块
产品有以下三种编译类型:
eng 工程机版本,开发阶段使用,有大量的调试程序
userdebug 用户调试机, 测试阶段使用, 有root权限,含调试测试程序
user 用户机, 最终用户使用
每个模块都存在标签:
eng 工程机使用的模块
debug 用于用户调试机的模块
tests 测试模块
samples 样例
optional 自由选择模块
空 没有标签
如何根据编译类型选择模块?
eng 编译标签为eng和debug的模块
产品模块(PRODUCT_PACKAGES)
userdebug 编译标签为debug的模块
产品模块(PRODUCT_PACKAGES)
user 产品模块(PRODUCT_PACKAGES)
(4) 编译总框架
build/core/Makefile(主要的编译规则)
build/core/main.mk(主要的Makefile)
(5) $(ONE_SHOT_MAKEFILE)
编译整个工程, 此变量为空
编译模块时,ONE_SHOT_MAKEFILE装载编译模块的Makefile文件, 文件名叫Android.mk
1. 如何管理模块?
(1) 一个模块一个Makefile文件(名称: Android.mk)
(2) 每个模块编译出来一个程序、库、Java包
2. 如何添加一个模块到Android系统?
(1) 添加程序或库或包的源代码
(2) 添加Android.mk
3. 如何编写Android.mk?
《Android.mk说明》
[4] 总结
1. 如何添加产品?(所有的源代码及配置文件使用fspad_723)
(1) 添加vendorsetup.sh文件
(2) 添加AndroidProducts.mk文件
(3) 添加T3.mk(拷贝参考产品)
PRODUCT_NAME
PRODUCT_DEVICE =
(4) 添加BoardConfig.mk(拷贝参考产品)
2. 如何添加一个模块?(apk)
Android.mk说明:
1. 设置当前模块的编译路径为当前文件夹路径
LOCAL_PATH := $(call my-dir)
2. 清理(可能由其他模块设置过的)编译环境中用到的变量
include $(CLEAR_VARS)
3. 模块编译变量
变量 用途
LOCAL_SRC_FILES 当前模块包含的所有源代码文件
LOCAL_MODULE 当前模块的名称,这个名称应当是唯一的,模块间的依赖关系就是通过这个名称来引用的
LOCAL_C_INCLUDES C/C++ 语言需要的头文件的路径
LOCAL_STATIC_LIBRARIES 当前模块在静态编译时,需要的静态库
LOCAL_SHARED_LIBRARIES 当前模块在运行时依赖的动态库
LOCAL_CFLAGS C/C++编译器的参数
include $(BUILD_EXECUTABLE)
gcc $(LOCAL_CFLAGS) $(LOCAL_SRC_FILES) -o $(LOCAL_MODULE) -I$(LOCAL_C_INCLUDES) $(LOCAL_STATIC_LIBRARIES) -l$(LOCAL_SHARED_LIBRARIES)
LOCAL_JAVA_LIBRARIES 当前模块依赖的Java共享库
LOCAL_STATIC_JAVA_LIBRARIES 当前模块依赖的Java静态库
LOCAL_PACKAGE_NAME 当前模块的APK应用的名称
LOCAL_CERTIFICATE 签署当前应用的证书名称
LOCAL_MODULE_TAGS 当前模块所包含的标签,Android.mk的必选,一个模块可以包含多个标签
标签的值可能是debug, eng, tests, samples 或 optional
build/core/definitions.mk
通常会用下面函数获取上面环境变量的值:
提供配置编译需要的函数
$(call my-dir) 获取当前文件夹路径
$(call all-subdir-java-files) 获取当前目录子目录下所有的java源代码文件
$(call all-java-files-under, 目录) 获取指定目录下的所有Java文件
$(call all-c-files-under, 目录) 获取指定目录下的所有C语言文件
$(call all-Iaidl-files-under, 目录) 获取指定目录下的所有 AIDL 文件
$(call all-makefiles-under, 目录) 获取指定目录下的所有Make文件
4. 模块类型(make.pdf)
include $(BUILD_%_%) %代码表0个或多个字符
BUILD_EXECUTABLE 编译目标机上的可执行文件(ELF)
BUILD_STATIC_LIBRARY 编译目标机上的静态库(*.a 编译时使用)
BUILD_SHARED_LIBRARY 编译目标机上的动态库文件(*.so)
BUILD_JAVA_LIBRARY 编译目标机上的java动态库
BUILD_STATIC_JAVA_LIBRARY 编译目标机上的java静态库
BUILD_PACKAGE 编译目标机上的java包
寻找Android.mk例子的方法:
find . -depth -name Android.mk -exec grep BUILD_STATIC_LIBRARY {} \;
产品信息文件说明:
在产品配置文件中, 存储产品信息,这些信息存放在变量中:
变量 说明
PRODUCT_COPY_FILES 编译该产品时需要拷贝的文件,以“源路径 : 目标路径”的形式
PRODUCT_PROPERTY_OVERRIDES 产品属性, 最终放入/system/build.prop
PRODUCT_PACKAGE_OVERLAYS 不修改packages中apk的情况下,自定义产品中的framework和package中的资源文件
DEVICE_PACKAGE_OVERLAYS 不修改packages中apk的情况下,自定义设备中的framework和package中的资源文件
PRODUCT_BRAND 产品商标
* PRODUCT_NAME 最终用户将看到的完整产品名,会出现在“关于手机”信息中
* PRODUCT_DEVICE 产品设备名
PRODUCT_MODEL 产品型号,最终用户将看到
PRODUCT_CHARACTERISTICS tablet-平板模式 phone-电话模式
PRODUCT_AAPT_CONFIG 指定支持哪些尺寸,哪些密度(dot per inch)的屏幕
PRODUCT_AAPT_PREF_CONFIG 指定屏幕尺寸及密度
android系统根据屏幕尺寸和密度来加载相应图片资源,使得android界面可以适用各种界面
具体见《Android屏幕规范.jpg》
PRODUCT_LOCALES 产品支持的地区,以空格分格
*PRODUCT_PACKAGES 产品版本中包含的应用程序, 以空格分格
PRODUCT_MANUFACTURER 产品厂家
PRODUCT_OTA_PUBLIC_KEYS 对于该产品的OTA公开key列表
PRODUCT_POLICY 产品使用的策略
PRODUCT_CONTRIBUTORS_FILE HTML文件, 包含项目的贡献者
PRODUCT_TAGS 产品标签,以空格分格
Android文件系统:
编译结果:
out
├── host SDK 中的各种工具(emulator,adb...)
│ ├── common 所有主机都用的通用库(java库)
│ │ └── obj
│ │ └── JAVA_LIBRARIES
│ └── Linux-x86 用linux-x86主机上工具程序
└── target 目标机上运行的各种程序
├── common 所有产品都用的通用程序(java程序或库)
│ ├── docs 文档目录
│ ├── obj 中间文件
│ │ ├── APPS 应用程序的中间文件
│ │ └── JAVA_LIBRARIES java库的中间文件
│ └── R 资源文件(java代码形式)
│ ├── android
│ ├── com
│ ├── jp
│ └── org
└── product 产品(特定平台)专用程序
└── fspad-723 fspad-723型号产品(全志A23平台)程序
├── data 用户数据, 该目录中的内容被挂载到/data目录下
├── obj
├── recovery 恢复版的根文件系统
├── root 根文件系统,装有最基本的命令, 该目录中的内容被挂载到/目录下
├── symbols
├── system 系统文件系统, 该目录中的内容被挂载到/system目录下
-------------------------------------------------------------
├── ramdisk.img root目录打包
├── ramdisk-recovery.img recovery目录打包
├── boot.img kernel + ramdisk.img
├── system.img system目录打包
├── userdata.img data目录打包
└── recovery.img kernel + ramdisk-recovery.img
为什么root目录打包后叫ramdisk.img?
(1) 什么是ramdisk?
用内存模拟磁盘存放文件系统, 内存称为ramdisk
(2) 为什么root目录打包后叫ramdisk.img?
root目录下的内容在运行时存放在内存
注意: root目录下存放的是根文件系统
为什么kernel + ramdisk.img被打包成boot.img?
android系统将kernel和根文件系统存放在一个分区,便于android系统启动时,一起拷贝
到内存,内核启动完成后,直接挂载根文件系统