【教程】Android(AOSP)Framework开发/ROM定制快速教程
备注一、基础知识二、基本操作1.源码获取2.编译刷机3.构建系统
三、系统结构1.系统架构2.目录结构3.源码结构
四、二次开发1.添加产品2.添加程序3.添加其他
五、启动流程1.BootROM阶段2.Bootloader阶段3.Kernel阶段4.init阶段5.Zygote阶段6.system_server阶段7.Launcher阶段
六、Binder1.原理2.组成3.流程4.AIDL
七、核心服务1.AMS2.PMS3.WMS
八、其他特性1.应用权限2.文件权限3.SELinux权限4.签名5.属性6.HAL
备注
2025/03/13 星期四 记录一下完整的Android系统开发知识,方便自己查阅
一、基础知识
Android是Google基于Linux内核研发的移动操作系统,Google将Android源码进行了开源称为AOSP(Android Open Source Project)。Android经过多年发展,除了手机还广泛应用于手表、平板、电视、车机等智能设备中。对AOSP源码做二次开发的工作一般称为Framework开发或者ROM定制。
Android设备制造行业一个基本的分工是: 1.Google开发AOSP 2.芯片厂商根据芯片适配AOSP(如高通、展锐、联发科、全志) 3.主板厂商(有的芯片厂商也当主板厂商)设计电路板,增加其他配件,在芯片厂商源码基础上继续修改做适配 4.设备制造商对主板厂商的源码定制UI、增加功能、优化系统(如华为、小米、OPPO、VIVO) 芯片厂商和主板厂商一般被称为vendor,设备制造商一般被称为oem或odm
另外,与传统固件(BIOS/UEFI、BootROM、硬件控制程序)概念不同,Android领域的固件很多时候也指包含了系统镜像、Linux内核、SE/TEE、Bootloader、Recovery等软件的线刷包。而“系统”多指基于AOSP修改得到的操作系统。
二、基本操作
1.源码获取
Google建议在Ubuntu上进行开发,提供了Android Studio for Platform作为开发工具。获取AOSP源码的操作如下:
# 安装基本依赖
sudo apt-get install git-core gnupg flex bison build-essential zip curl zlib1g-dev libc6-dev-i386 x11proto-core-dev libx11-dev lib32z1-dev libgl1-mesa-dev libxml2-utils xsltproc unzip fontconfig
# 安装源码管理工具repo
sudo apt-get install repo
# 初始化仓库
repo init --partial-clone -b [分支] -u https://android.googlesource.com/platform/manifest
# 拉取源码
repo sync -c -j8
2.编译刷机
对源码进行编译的操作如下:
source build/envsetup.sh
lunch
make -j$(nproc)
(注:Android中的内核文件是预编译好的,如果想要修改内核需要拉取对应的内核代码,修改编译后将编译结果放到指定路径,再重新编译打包Android镜像。) 编译完成后进行刷机的操作如下:
adb reboot bootloader
fastboot flashall -w
顺便一提,刷机的方式有fastboot、recovery、EDL和ota应用,这里我们选的是fastboot俗称线刷,也可以自行选用其他方式进行刷机(recovery俗称卡刷,EDL是紧急下载模式用于救砖,最知名是高通的9008,其他芯片厂商也有类似的工具,ota应用最常见的就是手机设置中的系统更新)
3.构建系统
Android提供了两种构建方式,在Android 7.0之前使用基于make的构建系统,使用Android.mk文件描make述构建规则,在Android 7.0后引入了soong构建系统,使用Android.bp文件描述soong的构建规则。soong中采用了kati GNU Make克隆工具和ninja后端来加速对系统源码的构建,用于解决make在Android中构建缓慢、容易出错、无法扩展、难以测试等问题。虽然make构建系统已经逐步被soong构建系统取代,但是仍然可以使用。 Android.mk:
// 设置当前构建所在目录,通常作为一个Android.mk文件中的第一行
LOCAL_PATH := $(call my-dir)
// 清空所有的LOCAL_变量,避免模块之间的变量相互干扰。
include $(CLEAR_VARS)
// 定义模块名称,必须是唯一不重复的,构建系统会根据类型自动添加前缀后缀
LOCAL_MODULE := my-module
// 模块类型(可选项),如APPS、 EXECUTABLES、SHARED_LIBRARIES、ETC
LOCAL_MODULE_CLASS := EXECUTABLES
// 模块所需全部源文件
LOCAL_SRC_FILES := src/test.cpp
// C/C++搜索头文件的路径
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
// 模块依赖的库文件(静态库.a和动态库.so)
LOCAL_STATIC_LIBRARIES:= lib1
LOCAL_SHARED_LIBRARIES:= lib2
// 编译选项
LOCAL_CFLAGS := -Wall -Wextra
LOCAL_CPPFLAGS := -std=c++11
// 构建类型,如BUILD_EXECUTABLE、BUILD_SHARED_LIBRARY、BUILD_STATIC_LIBRARY、BUILD_PACKAGE、BUILD_JAVA_LIBRARY、BUILD_PREBUILT
include $(BUILD_XXX)
Android.bp:
// 构建类型,如cc_binary、cc_library_static、cc_library_shared、android_app、java_library、java_library_static、prebuilt_apk
cc_library_shared {
// 模块名,如果是库文件通常约定加上'lib'前缀
name: "libdemo",
// 源文件列表
srcs: [
"src/test.c",
],
// C/C++搜索头文件的路径
include_dirs: ["include"],
// 编译选项
cflags: ["-Wall", "-Wextra"],
cppflags: ["-std=c++11"],
// 模块依赖的库文件(静态库.a和动态库.so)
shared_libs: [
"liblog",
"libcutils",
],
// 控制哪些模块可以依赖此模块,如:["//visibility:public"]、["//visibility:private"]、["//path/to:other_module"]
visibility: ["//visibility:public"],
}
另外Android提供了androidmk工具用于将Android.mk文件转换为Android.bp文件
三、系统结构
1.系统架构
Android分为5层结构,从上到下依次是应用层、系统框架层、原生库和运行时层、HAL层、内核层 应用层:用户直接与之交互的部分,包括各种应用程序和系统界面 框架层:提供Java/Kotlin类接口 原生库:C/C++ 库,一般是用于实现核心系统功能高性能的原生库 运行时:ART和Dalvik的虚拟机,用于执行dex文件 HAL:标准硬件接口,使Android以统一的方式调用硬件的功能 Linux内核:Android定制后的Linux内核,提供最基本的CPU调度、内存管理、文件系统等功能
2.目录结构
目录作用/acctLinux内核管理进程的CPU、内存、IO等资源的信息/cache系统临时更新和缓存/cache/backup备份目录/cache/recoveryrecovery目录/config/data用户数据目录/data/anr系统异常记录/data/adbadb数据目录/data/app存放用户安装的第三方应用app文件/data/dalvik-cache存放优化后的字节码,用于应用快速启动/data/data所有应用包括系统应用和第三方应用的私有数据/data/media内部存储/sdcard或/storage/emulated/0的实际挂载点/data/miscwifi、蓝牙、vpn、adb密钥等数据/data/propertypersist属性/data/system存放系统核心配置,如应用列表、账户信息、设备策略、锁屏设置等/data/user多用户的环境下指向/data/data的符号链接,用于实现多用户数据隔离/dev/mnt挂载外接设备/oem设备制造商目录/proc进程实时信息/rootroot用户家目录/sbinroot用户的二进制bin/sdcard符号链接,指向data/media或/storage/emulated/0/storage挂载所有存储设备的根目录/sysLinux内核的文件系统,提供虚拟文件系统、内核对象、设备驱动、硬件属性等/systemAndroid系统的核心文件/system/app存放Android的系统应用app/system/bin存放系统的命令行工具二进制elf/system/etc系统主机名、ip地址映射等配置文件/system/lib存放系统的动态库so/system/framework系统框架的核心jar/system/media存放系统铃声、提示音、开机动画等多媒体文件/system/fonts存放系统的字体/system/priv-app系统特权应用app/system/usr存放用户键盘布局、共享、时区等配置文件/system/xbin存放额外的命令行工具二进制elf
3.源码结构
目录作用art该目录是在Android 5.0中新增加的,主要是实现Android RunTime(ART)的目录,它作为Android 4.4中的Dalvik虚拟机的替代,主要处理Java字节码执行bionicAndroid的C库,包含了很多标准的C库函数和头文件,还有一些Android特有的函数和头文件build该目录包含了编译Android源代码所需要的脚本,包括makefile文件和一些构建工具compatibilityAndroid设备的兼容性测试套件(CTS)和兼容性实现(Compatibility Implementation)ctsAndroid设备兼容性测试套件(CTS),主要用来测试设备是否符合Android标准dalvikDalvik虚拟机,它是Android 2.3版本之前的主要虚拟机,它主要处理Java字节码执行developersAndroid开发者文档和样例代码development调试工具,如systrace、monkey、ddms等device特定的Android设备的驱动程序external第三方库,如WebKit、OpenGL等frameworksAndroid应用程序调用底层服务的APIhardwareAndroid设备硬件驱动代码,如摄像头驱动、蓝牙驱动等kernelAndroid系统内核的源代码,它是Android系统的核心部分libcoreAndroid底层库,它提供了一些基本的API,如文件系统操作、网络操作等packagesAndroid系统中的系统应用程序的源码,例如短信、电话、浏览器、相机等pdkAndroid平台开发套件,它包含了一些工具和API,以便开发者快速开发Android应用程序platform_testing测试工具,用于测试Android平台的稳定性和性能prebuilts预先编译的文件,如编译工具、驱动程序等sdkAndroid SDK的源代码,Android SDK的API文档、代码示例、工具等systemAndroid系统的核心部分,如系统服务、应用程序、内存管理机制、文件系统、网络协议等test测试代码,用于测试Android系统的各个组件toolchain编译器和工具链,如GCC、Clang等,用于编译Android源代码tools开发工具,如Android SDK工具、Android Studio、Eclipse等vendor硬件厂商提供的驱动程序,如摄像头驱动、蓝牙驱动等
四、二次开发
首先了解一下不同分区的作用,这里优先区分一下system、vendor、odm和product分区,
分区作用systemAOSP系统组件,所有product都通用的软件vendor芯片和主板厂商针对硬件开发的通用的可执行文件、库、系统服务和 app (不包含驱动)odm产品硬件差异导致的相关软件差异部分都会放在odm分区product软件差异都放在product分区
因此可以从软硬件、通用和差异方面简单理解为:
软件硬件通用systemvendor差异productodm
编译后的文件位置和编译命令如下:
分区Android.mkAndroid.bpsystem默认就是输出到 system 分区默认就是输出到 system 分区vendorLOCAL_VENDOR_MODULE := truevendor: trueodmLOCAL_ODM_MODULE := truedevice_specific: trueproductLOCAL_PRODUCT_MODULE := trueproduct_specific: true
1.添加产品
不同产品的源码会存在差异,通过配置文件来实现区分,这些配置文件称为 Product,每一个 Product 适用于特定的硬件产品,在编译时通过lunch进行选择。 Google提供的product 配置文件会保存在build/target目录下,芯片厂商或主板厂商提供的product配置文件在device目录下。 当我们想要添加自己的product 配置文件时一般也会选择在device目录下新增<公司名>/
PRODUCT_MAKEFILES := \
$(LOCAL_DIR)/
COMMON_LUNCH_CHOICES := \
# 基本信息
PRODUCT_NAME :=
PRODUCT_DEVICE :=
PRODUCT_BRAND := <公司名>
PRODUCT_MODEL := <机型名>
# 引用其他配置
$(call inherit-product,
# 构建类型
PRODUCT_BUILD_VARIANT := user
#是否使用自定义内核
TARGET_NO_KERNEL_OVERRIDE := true
# 分区配置
PRODUCT_SYSTEM_PROPERTIES += \
ro.system.size=4G
BOARD_SYSTEMIMAGE_PARTITION_SIZE := 4294967296
# 添加程序
PRODUCT_PACKAGES += \
# 添加属性
PRODUCT_PROPERTY_OVERRIDES := \
persist.sys.flag=1 \
ro.control.flag=0
# 添加文件
PRODUCT_COPY_FILES += \
$(LOCAL_PATH)/
BoardConfig.mk是定义硬件底层配置、芯片架构、分区大小、bootloader 和 kernel, 是否支持摄像头,GPS导航等一些板级特性的文件。
2.添加程序
Android中有很多bin目录,其中都是一些二进制可执行文件或shell脚本,这些程序源码主要来自于external、system/core、frameworks/native/cmds和frameworks/base/cmds,这里我们参考已有工具新建工具名目录、编写Android.bp文件、新建src/工具名目录并编写源码,在
PRODUCT_PACKAGES += 程序名
如果直接编译文件会被放到system分区,还需要在
PRODUCT_ARTIFACT_PATH_REQUIREMENT_WHITELIST += \
system/bin/程序名\
system/app/程序名/程序名.apk \
system/lib/lib库名.so \
但是我们编写的工具大概率是产品相关的官方建议放到product分区下,Android.bp中添加product_specific: true即可
3.添加其他
类似的,还可以去添加库文件、配置文件、app等,方法大多差不多,只有一些细节差别,这里不过多赘述可以自行搜索
五、启动流程
Android系统启动可以分为七个阶段:BootROM阶段、Bootloader阶段、Kernel阶段、init阶段、zygote阶段、System Server阶段和Launcher阶段。
1.BootROM阶段
BootROM阶段在设备通电后,由SoC芯片内置只读程序初始化最基础的硬件(如CPU核心、时钟),查找并验签位于特定位置的Bootloader程序,如果验签通过则会加载Bootloader程序
2.Bootloader阶段
Bootloader一般分为两级,由BootROM加载的称为一级导加载程序(PBL),一级引导加载程序负责初始化更多基础硬件(如DRAM、eMMC/UFS、显示器),查找并验签位于特定分区的二级引导加载程序(SBL),如果验签通过则会加载二级引导加载程序。 二级引导加载程序继续初始化更多硬件,验签启动的镜像boot.img,如果验签通过则会加载boot.img到内存中。
3.Kernel阶段
Bootloader将boot.img 中的内核加载到内存中,CPU将控制权从Bootloader转交给内核,内核会初始化各类硬件设备、加载驱动程序、管理内存中断信号等。内核还会加载boot.img中一个最小临时文件系统initramfs,然后启动init进程
4.init阶段
init进程是Android系统中的第一个用户空间进程,PID为1。init进程又会读取init.rc文件,根据该文件中的配置信息加载正式的文件系统、启动并配置SELinux、启动Android系统的各个组件。init进程启动流程可以分为三个主要阶段:第一阶段初始化(first_stage_init)、SELinux设置(selinux_setup)二阶段初始化(second_stage_init)。第一阶段初始化主要是挂载分区,SELinux设置阶段会初始化SELinux相关的内容,第二阶段初始化完成进程和服务的启动。 init进程刚开始运行的时候是内核态,然后运行一个用户态程序把自己强行转成用户态,后面的操作全在用户态下进行,当init进程从内核态跳到用户态后,用户态下的程序无法再回到内核态,想要进入内核态只能通过系统调用。 init.rc文件是一个专门用于Android inti的配置文件,有三类语句:On、Service、Import。On是在Action的情况下执行Command,Service是定义服务的名称、路径、启动参数和配置项,Import是引入其他rc文件:
import /init.environ.rc
on
...
service
...
5.Zygote阶段
Zygote是由init启动的进程,Zygote负责预加载核心资源(framework.jar, framework-res.apk 等)、 初始化 Android Runtime(ART),启动 system_server(com.android.server.SystemServer)。
6.system_server阶段
system_server是Zygote fork出来的第一个进程,负责启动和管理几乎所有关键native和java服务(如AMS、PMS、WMS、LocationManagerService、TelephonyManagerService、Wi-FiService、BluetoothService等),system_server启动完成后会通过广播所有已启动的服务。
7.Launcher阶段
AMS收到system_server发送的启动完成广播后会启动 Launcher,Android系统就完全启动了,用户可以进入桌面使用各种应用程序。
六、Binder
1.原理
Binder是Android为了代替Linux IPC设计的,是具有远程过程调用RPC(Remote Procedure Call)能力的进程间通信IPC(Inter-Process Communication)机制。为了保证不同进程之间互不干扰,Linux为每个进程都分配有独立的虚拟内存空间,不同进程之间的用户空间相互独立实现进程隔离,系统上的所有进程共用一个内核空间,如果进程间想要相互访问数据就需要借助内核空间来实现。将数据从一个进程的用户空间复制到内核空间,再从内核空间将数据复制到另一个进程的用户空间,就可以达到跨进程访问数据的目的。但是需要做两次复制操作,如果将一个进程用户空间虚拟地址直接映射到内核空间的物理地址,再让另一个进程从内核空间复制数据,就可以优化为一次复制操作,这就是Binder的一次复制原理。 同时Binder提供了RPC能力。RPC就是像调用本地函数一样调用远程函数(远程包括逻辑和隔离和物理隔离,即本机其他进程或者网络上其他机器的进程),因此需要将数据按照一定的规则打包并发送给目标进程,目标进程解析数据执行函数后再将结果按一定格式发回给调用者。 Android中常见的IPC机制如Intent、Messenger、ContentProvider、AIDL底层都是由Binder实现的。
2.组成
Android的Binder主要包括了Client、Server、ServiceManager和Binder驱动四个部分。 Client是发起调用的进程 Server是提供Service的进程,Service是被调用的函数集合 ServiceManager是用于管理Service的进程,Server需要将Service注册到ServiceManager才能被Client调用 Binder驱动是内核层的特殊字符设备驱动,用于实现IPC需要具有访问内核空间的能力,但是Android并不属于Linux内核,不能直接访问内核空间,因此使用Linux的动态可加载内核模块(Loadable Kernel Module,LKM)的机制,将Binder驱动单独编译后再链接到内核中。
3.流程
Binder的核心流程包括:系统启动ServiceManager、Server注册Service、Client调用Service
4.AIDL
由上文可见使用binder需要涉及到内核驱动、naive层、java层非常复杂,Android提供了AIDL用于简化binder使用
七、核心服务
1.AMS
2.PMS
3.WMS
八、其他特性
1.应用权限
2.文件权限
3.SELinux权限
4.签名
5.属性
6.HAL