【教程】Android(AOSP)Framework开发/ROM定制快速教程

备注

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不允许将所有驱动嵌入到内核饼禁用了模块动态加载机制,而是定义了一个API,硬件供应商必须提供一个软件模块负责实现在定义的API)
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应用程序调用底层服务的API
hardwareAndroid设备硬件驱动代码,如摄像头驱动、蓝牙驱动等
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.bp
system默认就是输出到 system 分区默认就是输出到 system 分区
vendorLOCAL_VENDOR_MODULE := truevendor: true
odmLOCAL_ODM_MODULE := truedevice_specific: true
productLOCAL_PRODUCT_MODULE := trueproduct_specific: true

1.添加产品

不同产品的源码会存在差异,通过配置文件来实现区分,这些配置文件称为 Product,每一个 Product 适用于特定的硬件产品,在编译时通过lunch进行选择。
Google提供的product 配置文件会保存在build/target目录下,芯片厂商或主板厂商提供的product配置文件在device目录下。
当我们想要添加自己的product 配置文件时一般也会选择在device目录下新增<公司名>/<Product名>,再添加AndroidProducts.mk、<Product名>.mk、BoardConfig.mk,可以参考AOSP原生文件进行编写。
AndroidProducts.mk是由构建系统自动扫描的入口文件,基本内容如下:

PRODUCT_MAKEFILES := \
    $(LOCAL_DIR)/<Product名>.mk \ 

COMMON_LUNCH_CHOICES := \
    <Product名>-user \
    <Product名>-userdebug \
    <Product名>-eng

<Product名>.mk是产品的核心配置文件,用于定义产品的基础信息、引用其他配置文件、设置系统分区、预装程序、系统属性等,基本内容如下:

# 基本信息
PRODUCT_NAME := <Product名>
PRODUCT_DEVICE := <Product名>
PRODUCT_BRAND := <公司名>
PRODUCT_MODEL := <机型名>

# 引用其他配置
$(call inherit-product, <path/file.mk>)

# 构建类型
PRODUCT_BUILD_VARIANT := user
#是否使用自定义内核
TARGET_NO_KERNEL_OVERRIDE := true

# 分区配置
PRODUCT_SYSTEM_PROPERTIES += \
    ro.system.size=4G
BOARD_SYSTEMIMAGE_PARTITION_SIZE := 4294967296

# 添加程序
PRODUCT_PACKAGES += \
    <XXX> \
    <YYY> \
    <ZZZ>

# 添加属性
PRODUCT_PROPERTY_OVERRIDES := \
    persist.sys.flag=1 \
    ro.control.flag=0

# 添加文件
PRODUCT_COPY_FILES += \
    $(LOCAL_PATH)/<source_path/source_file>:$(TARGET_COPY_OUT_SYSTEM)/<target_path/target_file>

BoardConfig.mk是定义硬件底层配置、芯片架构、分区大小、bootloader 和 kernel, 是否支持摄像头,GPS导航等一些板级特性的文件。

2.添加程序

Android中有很多bin目录,其中都是一些二进制可执行文件或shell脚本,这些程序源码主要来自于external、system/core、frameworks/native/cmds和frameworks/base/cmds,这里我们参考已有工具新建工具名目录、编写Android.bp文件、新建src/工具名目录并编写源码,在<Product名>.mk文件中添加

PRODUCT_PACKAGES += 程序名

如果直接编译文件会被放到system分区,还需要在<Product名>.mk文件中指定目标位置才不会报错

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 <trigger>
    <command1>
    <command2>
    ...
    <commandn>

service <service_name> <path> [ <argument> ]
    <option1>
    <option2>
    ...
    <optionn>

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值