(学习课程为千里马framework的教程,整理成笔记自用)
代码路径介绍:
bootanimation frameworks/base/cmds/bootanimation/
surfaceflinger frameworks/native/services/surfaceflinger/
init system/core/init/
bootanimation是绘制图像的,bootanimation是依赖 surfaceflinger的,
所以 bootanimation 需要等 surfaceflinger启动以后才能进行绘制
整体流程大致为:
1:init进程启动:解析init.rc,启动 surfaceflinger
2:surfaceFlinger初始化:设置属性触发bootanim服务启动
3:init响应属性:通过属性服务启动bootanim进程
4:BootAnimation:通过SurfaceFlinger渲染动画,监听退出信号
首先要先启动 surfaceflinger
1: 系统启动时,init进程会读取 init.rc文件 , init.rc文件中定义了 Surfaceflinger服务。
service surfaceflinger /system/bin/surfaceflinger
...
init进程会根据 init.rc中的配置,在系统启动时触发Surfaceflinger的启动。
(SurfaceFlinger的主要任务:
1:初始化图形硬件
2:创建显示缓冲区
3:管理显示内容的合成和输出
)
这里init.rc文件应该也会触发bootanim.rc文件,但是bootanim.rc文件中设置了
service bootanim /system/bin/bootanim
...
disable (在开机时不会启动)
...
bootanim的主要任务:
1:通过SurfaceFlinger创建显示Surface;
2:加载并显示开机动画资源
3:在系统启动完成后退出
执行到这里,表示surfacefligner 服务已经启动了,接下来进入下一步,进入surfaceflinger主函数部分, main_surfaceflinger.cpp是SurfaceFlinger进程的入口
2. SurfaceFlianger的启动与初始化
在 main_surfaceflinger.cpp 中找到主函数
ramework/native/services/surfaceflinger/main_surfaceflinger.cpp
int mian (int , char**) {
...
sp<SurfaceFlinger> flinger = new SurfaceFlinger(); //创建实例
//main()函数创建SuerfaceFlinger,实例并调用 init() 和 run()
//初始化硬件
flinger -> init(); //初始化HWC、启动属性线程
//注册到ServiceManger
sp<IServiceManager> sm(defaultServiceManager());
//注册服务
sm->addService(String16(SurfaceFlinger::getServiceName()),flinger,false);
//进入主循环
flinger -> run(); //处理VSync和合成任务
..
}
这样做的目的:提供图形合成和核心能力,管理屏幕显示内容
关键方法:
1: new SurfaceFlinger()
作用:创建SurfaceFlinger对象,负责管理图形显示合成(Compositor)的核心服务
为什么要创建SurfaceFlinger()方法: SurfaceFlinger是Android图形系统的画像,
负责将应用层的Surface合成到屏幕
2: init():
作用:初始化硬件图形(HWC)、显示配置,并启动属性设置线程
3: run():
作用:进入主事件循环,处理VSync(垂直同步)信号和图形合成任务
为什么需要: SurfaceFlinger需要持续监听显示事件,确保帧按屏幕刷新合成
下一步进入SurfaceFlinger.cpp文件中,因为从上述代码中得知:
flinger -> init();
flinger -> run();
进入SurfaceFlinger,找到init()方法,开始执行init()方法
(这里放入全部init()方法中全部代码的原因,是我不理解为什么只有
if (getHwComposer().hasCapability(
HWC2::Capability::PresentFenceIsNotReliable)) {
mStartPropertySetThread = new StartPropertySetThread(false);
} else {
mStartPropertySetThread = new StartPropertySetThread(true);
}if (mStartPropertySetThread->Start() != NO_ERROR) {
ALOGE("Run StartPropertySetThread failed!");
}
是关键方法,其他方法又起到了什么作用
这一段代码是触发开机动画的关键代码,其他部分开机动画的启动起到了什么作用,所以下面附上init()部分的全部代码,对其整体进行简单分析:
SurfaceFlinger.cpp中init()方法的调用
默认显示初始化EGL
//作用:初始化EGL ,创建OpenGL ES的显示连接和上下文
//与动画关系:EGL 是 OpenGL ES的渲染基础,Bootanimation通过SurfaceFlinger获取 Surface提交
//帧数据时,依赖EGL上下文进行图形绘制
(代码如下:下同)
mEGLDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
eglInitialize(mEGLDisplay, NULL, NULL);
start the EventThread 开始线程
//创建 Vsync事件线程
//作用:创建两个VSync事件线程:
// mEventThread:处理应用层的 VSync信号(如Bootanimation的帧提交)
// mSREventThread:处理SurfaceFlinger自身的合成任务
//与动画的关系:
// Bootanimation按屏幕刷新率提交帧,依赖VSync信号同步渲染,避免屏幕撕裂
sp<VSyncSource> vsyncSrc = new DispSyncSource(&mPrimaryDispSync,
vsyncPhaseOffsetNs, true, "app");
mEventThread = new EventThread(vsyncSrc, *this, false);
sp<VSyncSource> sfVsyncSrc = new DispSyncSource(&mPrimaryDispSync,
sfVsyncPhaseOffsetNs, true, "sf");
mSFEventThread = new EventThread(sfVsyncSrc, *this, true);
mEventQueue.setEventThread(mSFEventThread);
// set EventThread and SFEventThread to SCHED_FIFO to minimize jitter
//设置实时调度策略
//作用:将VSync线程的调度策略设为 SCHED_FIFO(实时优先级),确保VSync信号处理的递延迟
//与动画的关系:保证动画帧率稳定,避免因为线程调度延迟导致动画卡顿
struct sched_param param = {0};
param.sched_priority = 2;
if (sched_setscheduler(mSFEventThread->getTid(), SCHED_FIFO, ¶m) != 0) {
ALOGE("Couldn't set SCHED_FIFO for SFEventThread");
}
if (sched_setscheduler(mEventThread->getTid(), SCHED_FIFO, ¶m) != 0) {
ALOGE("Couldn't set SCHED_FIFO for EventThread");
}
//初始化硬件合成器(HWComposer)
//作用:初始化硬件合成器(HWComposer),与显示硬件和驱动交互,管理显示缓冲区
//与动画关系:HWC负责将BootAnimation的帧数据最终提交到屏幕,硬件加速合成可降低CPU/GPU负载
mHwc.reset(new HWComposer(false));
mHwc->registerCallback(this, mComposerSequenceId);
// set initial conditions (e.g. unblank default device)
//初始化显示设备
//初始化默认显示设备(如主屏幕),设置分辨率和显示模式
//与动画关系:确保动画按屏幕物理分辨率渲染,避免拉伸或黑边
initializeDisplays();
// Inform native graphics APIs whether the present timestamp is supported:
//启动属性设置线程(StartPropertySetThread)
//作用:根据硬件合成器的能力(是否支持可靠的PresentFence),设置属性
//service.sf.present_timestamp , 并触发bootanim服务启动
//关键点在于:
// PresentFence:用于同步帧提交与屏幕刷新,若不可靠,需禁用时间戳相关优化
// service.bootanim.exit = 0: 允许动画启动
// ctl.start = bootanim : 通过init()线程启动 bootanim服务
if (getHwComposer().hasCapability(HWC2::Capability::PresentFenceIsNotReliable)) {
mStartPropertySetThread = new StartPropertySetThread(false);
} else {
mStartPropertySetThread = new StartPropertySetThread(true);
}
if (mStartPropertySetThread->Start() != NO_ERROR) {
ALOGE("Run StartPropertySetThread failed!");
}
ALOGV("Done initializing");
(这段代码是触发bootanim的关键点, mStartPropertySetThread -> Start() 启动属性设置线程)
初始化硬件(HWC2),根据硬件composer的能力来决定是否支持可靠的呈现时间戳。
调用getHwComposer()函数,获取硬件composer实例
使用hasCapability()函数检查composer是否具有 HWC2::Capability::PresentFencelsNotReliable能力。
不可靠,给mStartPropertySetThread传入一个false
可靠,给mStartPropertySetThread传入一个true
Start函数的返回值被与NO_ERROR进行比较,以检查线程启动是否成功。如果Start函数返回的值不等于NO_ERROR
则说明线程启动失败,程序将执行if语句块中的来处理这个错误
针对SurfaceFlinger.cpp中init()方法关于开机动画调用情况的总结:
一:除了关键bootanim的触发点以外,其他部分的代码也是必要的:
1:EGL和RenderEngine:
--提供OpenGL ES渲染上下文,Bootanimation需要将图片帧渲染到Surface,依赖EGL和
RenderEngine的GPU加速能力
2:VSync事件线程
--确保动画按屏幕刷新率提交帧,避免丢帧或卡顿
3:HWComposer:
--管理显示缓冲区,硬件加速合成动画帧,降低CPU/GPU负载。
4:显示设备初始化:
--设置正确的分辨率和显示模式,保证动画全屏显示
二:开机动画的完整依赖链
SurfaceFlinger 初始化
├─ EGL/RenderEngine → 提供 GPU 渲染能力
├─ HWComposer → 硬件加速合成
├─ VSync 线程 → 同步帧率
├─ 显示设备初始化 → 设置分辨率
└─ StartPropertySetThread → 触发动画启动
----直接触发:StartPropertySetThread是动画开启的“开关”;
----间接依赖:其他代码为动画提供运行环境(e.g. 渲染、同步、显示)
三:总结
1- StartPropertySetThread的作用
仅负责通过属性服务触发 bootanim 服务器动,是动画流程的“点火器”
2- 其他代码的作用:
构建SurfaceFlinger的图形处理基础设施,为动画渲染、同步、合成提供底层支持。
3- 全部代码的作用
开机动画是SurfaceFlinger功能的一个用例,其正常运行依赖完整的图形子系统初始化。
mStartPropertyThread -> Start() 触发了动画启动的开关,进入 StartPropertyThead.cpp文件中,调用Start方法。现在进入StartPropertyThread.cpp文件中查看具体情况
进入StartPropertyThread.cpp文件
这个类属于Android命名空间,这个类继承自Thread,构造函数接受了一个布尔参数“timestampProeprtyValue",用来初始化成员变量, 接下来是Start()方法的重载,以及threadLoop()方法的实现。
首先,构造函数StartProeprtyThread::StartPropertySetThread(bool timestampPropertyValue()).这里调用基类Thread的构造函数(Thead(false)),参数是false,可能表示线程不是可加入的(joinable)。 然后初始化成员变量mTimetampPropertyValue,这个变量用于后续设置属性。
然后Start()方法,这是线程的主循环,在Andorid的线程模型中,thread()返回true会继续循环,返回false则线程结束。这里返回false,表示线程只会执行一次就退出。
第一个属性kTimetampProperty根据mTimetampPropertyValue设置为 1 或 0 , 这个属性的作用是告诉系统是否支持呈现时间戳。
第二个属性 service.bootanim.exit 设置为 0 ,表示允许开机动画运行
第三个属性 ctl.start 设置为 bootanim,触发init进程启动bootanim服务
1:继承自Thread类, Thread(false)调用基类构造函数,参数false表示线程是不可加入(non-joinable)的。
--不可加入线程:线程结束后自动释放资源,无需调用join()等待其结束。
2:初始化成员变量
--mTimestampPropertyValue是一个布尔值,用于后续设置service.sf.present_timestamp属性
在开机动画中的作用:
--根据硬件合成器(HWC)的能力(是否支持可靠的PresentFence),决定是否启动时间戳属性。
StartPropertySetThread::StartPropertySetThread(bool timestampPropertyValue):
Thread(false), mTimestampPropertyValue(timestampPropertyValue) {}
此参数在SurfaceFlinger::init()中通过 getHwComposer().hasCapability(...)判断后传入。
/Start()方法:
//1:启动线程
--run()是Thread类的方法,启动新线程并执行threadLoop()
//2:参数说明
--线程优先级:SurfaceFlinger::StartPropertySetThread , 用于调用调试和日志标识
--优先级:PRIORITY_NORMAL表示普通优先级,确保线程不会过度占用CPU
//3:在开机动画中的作用
--启动线程后,立即执行threadLoop()方法设置关键属性,触发开机动画
status_t StartPropertySetThread::Start() {
return run("SurfaceFlinger::StartPropertySetThread", PRIORITY_NORMAL);
}
线程主循环
bool StartPropertySetThread::threadLoop() {
// Set property service.sf.present_timestamp, consumer need check its readiness
//1:设置service.sf.present_timestamp:
// --属性名:kTimestamProperty(实际值为"service.sf.present_timestamp")
// --属性值:根据mTimestampPropertyValue 这是为 1 或 0
// --作用:
// 1 表示系统支持可靠的呈现时间戳(Present Timestamp)
// 0 表示不支持
property_set(kTimestampProperty, mTimestampPropertyValue ? "1" : "0");
// Clear BootAnimation exit flag
//2:设置service.bootanim.exit = 0
// --作用:通知开机动画进程(bootanim)可以运行
// --动画进程逻辑:
// bootanim会周期性检查此属性,如果值为 1 ,则退出动画
property_set("service.bootanim.exit", "0"); //允许动画运行
// Start BootAnimation if not started
//3:设置 ctl.start = bootanim
// --作用:通过init进程启动bootanim服务
// --init进程响应机制:
// init监听 ctl.start属性,解析到bootanim后,从服务列表中找到对应服务并启动。
property_set("ctl.start", "bootanim"); //触发bootanim服务启动
//4:返回false
// --线程行为:threadLoop() 返回 false表示线程仅执行一次后退出
return false; //线程仅执行一次
}
在开机动画中的作用:
1:配置时间戳支持状态
2:允许动画运行
我们再梳理一遍处理流程
第一步:property_set()的实现:
--底层逻辑:
property_set()通过Unix Domain Socket 和 init 进程通信,写入属性值
init进程的property_service模块监听Socket,处理属性请求。
第二步:init进程堆 ctl.start的处理
--接受属性
init和handle_property_set_fd()监听到ctl.start = bootanim
--解析命令:
调用 init.cpp 中的 handle_control_message()方法
handle_control_message()
...
};
--启动服务
从服务列表中找到bootanim,调用Service::Start()派生子进程执行
第三步:bootanim对service.bootanim.exit的响应
动画进程逻辑
//Bootanimation.cpp文件中
void BootAnimation::checkExit(){
...
property_get(EXIT_PROP_NAME,value,"0");
...
}
动画循环中,周期性检查此属性,实现退出控制。
那么问题来了,为什么这里property_set一设置,bootanim就会启动,上面说到,两个property_set设置处,init进程会对两个设置进行监听。
第一个property_set中设置了 service_bootanim.exit = 0 , 表示运行开机动画启动;
第二个property_set中设置了 ctl.start = bootanim, init的handle_contorl_message()获取到了这个属性变化;
那么下一步,先针对property_set()关键代码的调用继续梳理,再回到init.cpp 中继续跟踪。
ctl.start = bootanim的处理流程
第一步:属性设置请求接收
1:其他进程调用property_set("ctl.start","bootanim");
e.g. SurfaceFlinger的StartPropertySetThread设置此属性
2:写入到Socket中
通过Unix Domain Socket /dev/socket/property_service发送属性名和值第二步:init进程的时间循环响应
1:epoll_wait监听到Socket事件:
-当ctl.start = bootanim写入到Scoket时,epoll_wait返回,触发回调函数
handle_property_set_fd.
2:调用 handle_property_set_fd():
//读取属性名和值第三部:处理控制命令 ctl.start
1:handle_property_set()解析控制命令(代码在propert_service.cpp文件中):
static void handle_property_set(...) {
if (name.starts_with("ctl.")) {
handle_control_message(name.substr(4), value); // 提取 "start" 和 "bootanim"
}
}
2:handle_control_message()启动服务(代码在init.cpp文件中):
void handle_control_message(const string& msg, const string& name) {
Service* svc = ServiceManager::GetInstance().FindServiceByName(name);
if (msg == "start") svc->Start(); // 调用 Service::Start()
}
其关键调用链为:
1. main()第二阶段初始化
-> property_int()
->start_property_service() -> 创建 Socket,注册epoll监听
2. 其他进程设置属性:
-> property_set("ctl.start","bootanim");
3. init进程响应
-> epoll_wait 监听到 Socket事件
-> handle_property_set_fd() -> 解析属性名和值
-> handle_property_set() -> 处理控制命令
-> handle_control_message("start","bootanim"); ->提取接收到的信息
4. 服务启动
-> ServiceManager::FindServiceByName("bootanim");
-> Service::Start() -> fork() + execve("/system/bin/bootanimation");
这里为了衔接后续 init.cpp 文件里相关方法的调用以及 property_service方法的调用。有必要说明整个调用链是什么。
下一步我们再次回到 init.cpp 文件中,查看其在main方法中的调用流程
说明:这里的分析只针对init 对ctl.start的处理
init.cpp文件中,ctl.start的处理流程
第一步:初始化属性
//初始化属性系统
property_init();
//启动服务属性(创建 Socket 并注册 epoll 监听)
//这里调用了property_service.cpp文件中的start_property_service()方法
//---> 去property_service.cpp中查看start_property_service()的调用链
start_property_service(); //启动属性服务
通过epoll监听该Socket的事件,注册回调函数 handle_property_set_fd.
第二步:处理属性设置请求
进入property_serivce.cpp文件中,执行start_property_service()
代码位于 /system/core/init/property_service.cpp中
void start_property_service() {
//设置属性服务的版本号 2,有助于系统跟踪兼容性或在调试时识别服务状态
property_set("ro.property_service.version", "2");
//参数解析:
// PROP_SERVICE_NAME:Socket路径为/dev/socket/property_service
// SOCK_STREAM:面向连接的流失Socket,确保可靠传输
// SOCK_CLOEXEC:进程执行exec时关闭Socket,避免泄漏
// SOCK_NONBLOCK:非阻塞冒失,防止进程在等待连接时被挂起
// 0666:权限设置为所有用户可读写
//作用:
// 创建一个用于跨进程通信的Socket,其他进程通过此Socket与init进程交互以设置或读取属性
//获取了一个property_set_fd
property_set_fd = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
false, 0666, 0, 0, nullptr, sehandle);
if (property_set_fd == -1) {
PLOG(ERROR) << "start_property_service socket creation failed";
exit(1);
}
//一直在监听property_set_fd
//listen(property_set_fd, 8);
//开始监听Socket,允许最多8个未决连接,
// --- 这使得其他进程可以通过此Socket与属性服务通信
//将Socket的文件描述符注册到init进程的epoll实例中,并绑定回调函数
//handle_property_set_fd:
//当Socket有新的连接数据到达时,epoll_wait会触发handle_property_set_fd处理请求
//handle_property_set_fd:负责解析属性请求,验证权限后调用property_set修改属性
//(register)注册
//这里register_epoll_handler会一直监听handler_property_set_fd有没有变化,如果有变化
//就会回调handler_property_set_fd方法
register_epoll_handler(property_set_fd, handle_property_set_fd);
}
第三步:回调handle_property_set_fd方法,那么我们进入到property_service.cpp的hander_property_set_fd方法中继续跟踪
property_service.cpp文件位置,/systm/core/init/property_service.cpp
static void handler_property_set_fd(){
//模式超时时间为2000毫秒,防止客户端长时间不发送数据导致阻塞
static constexpr uint32_t kDefaultSocketTimeout = 2000; /* ms */
//接受连接并分析命令
int s = accept4(property_set_fd, nullptr, nullptr, SOCK_CLOEXEC);
struct ucred cr;
getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cr_size);
SocketConnection socket(s, cr);
uint32_t cmd;
socket.RecvUint32(&cmd, &timeout_ms);
...
//解析属性名和值
case PROP_MSG_SETPROP: {
char prop_name[PROP_NAME_MAX];
char prop_value[PROP_VALUE_MAX];
if (!socket.RecvChars(prop_name, PROP_NAME_MAX, &timeout_ms) || //name = "ctr.start"
!socket.RecvChars(prop_value, PROP_VALUE_MAX, &timeout_ms)) { //value = "bootanim"
PLOG(ERROR) << "sys_prop(PROP_MSG_SETPROP): error while reading name/value from the socket";
return;
}
prop_name[PROP_NAME_MAX-1] = 0;
prop_value[PROP_VALUE_MAX-1] = 0;
handle_property_set(socket, prop_value, prop_value, true);
break;
}
...
}
到这里 handle_property_set方法通过soket跨进程通信获取到了 prop_name = ctr.start 和 prop_value = bootanim
下一步,调用property_servcie.cpp中的handle_property_set方法。
//权限验证
//权限检查
// --check_control_mac_perms():验证客户端(SurfaceFlinger)是否有权执行
// --SELinux策略:确保SurfaceFlinger的上下文允许发送
// ctl.*属性。
static void handle_property_set(SocketConnection& socket,
const std::string& name,
const std::string& value,
bool legacy_protocol) {
..........
char* source_ctx = nullptr;
getpeercon(socket.socket(), &source_ctx);
if (android::base::StartsWith(name, "ctl.")) {
if (check_control_mac_perms(value.c_str(), source_ctx, &cr)) {
handle_control_message(name.c_str() + 4, value.c_str());
if (!legacy_protocol) {
socket.SendUint32(PROP_SUCCESS);
}
}
通过:handle_control_message(name.c_str() + 4, value.c_str());的上下文得知,在这个判断语句中, 检查了一下控制,然后调用了 handle_control_message()方法,并将通过Socket获取到的 name 和 value 变化的值传入其中。
那么我们下面,再去handle_control_message()方法中看一下做了那些操作
handle_control_message()方法在 /system/core/init/init.cpp文件中,那么我们再回到init.cpp文件中看一下
//处理控制命令
//这里会传入的参数 msg.name=ctl.star
void handle_control_message(const std::string& msg, const std::string& name) {
//该函数首先调用FindServiceByName,从service_list中查询
Service* svc = ServiceManager::GetInstance().FindServiceByName(name);
if (svc == nullptr) {
LOG(ERROR) << "no such service '" << name << "'";
return;
}
if (msg == "start") {
// msg 获取到 start , 开始启动服务
svc->Start();
} else if (msg == "stop") {
svc->Stop();
} else if (msg == "restart") {
svc->Restart();
} else {
LOG(ERROR) << "unknown control msg '" << msg << "'";
}
}
//查询服务:从init.rc定义的bootanim服务,获取其Service对象。
//启动服务:调用Service::Start(),派生子进程 /system/bin/bootanimation
//查询服务:从init.rc定义的bootanim服务,获取其Service对象。
//启动服务:调用Service::Start(),派生子进程 /system/bin/bootanimation
这里就相当于手动启动 bootanimation , 前面bootanim.rc文件中设置类disable , 就是开机时不会自行启动bootanimation,所以需要这里通过SurfaceFlinger调用bootanimation手动启动,才能进入bootanimation绘制.
总结:
init启动 ---> SurfaceFlinger初始化 ---> 属性触发 ---> bootanim启动
下面就到了bootanimation的实现
frameworks/base/cmds/bootanimation/bootanimation_main.cpp
int main(int argc, char** argv)
{
sp<ProcessState> proc(ProcessState::self());
ProcessState::self()->startThreadPool();
// create the boot animation object
sp<BootAnimation> boot = new BootAnimation();//创建BootAnimation实例
//加入Binder通信
IPCThreadState::self()->joinThreadPool();//binder线程池,与surfaceflinger通信用的。
}
return 0;
}