Android 开机动画启动流程 (android 10)
1 开机动画启动流程
我们先来看一下开机动画是如何启动,并开始播放的。
通过系统启动流程分析可以得知,在系统内核启动后,会启动第一个init进程,init进程会扫描、解析init.rc文件,在init.rc文件中,会启动 surfaceflinger 进程, 在surfaceflinger 进程的main函数中会进行 SurfaceFlinger binder服务的启动,开机动画的相关流程正是在其中进行的,我们下面将会进行分析。
Android.bp 文件
# frameworks/native/services/surfaceflinger/Android.bp
filegroup {
name: "surfaceflinger_binary_sources",
srcs: ["main_surfaceflinger.cpp"],
}
cc_binary {
name: "surfaceflinger",
defaults: ["libsurfaceflinger_binary"],
# rc 文件引入,会将 surfaceflinger.rc 文件拷贝到指定目录进行解析
init_rc: ["surfaceflinger.rc"],
srcs: [":surfaceflinger_binary_sources"],
shared_libs: [
"libsurfaceflinger",
"libSurfaceFlingerProp",
],
}
//...
surfaceflinger.rc 文件
# frameworks/native/services/surfaceflinger/surfaceflinger.rc
service surfaceflinger /system/bin/surfaceflinger
class core animation
user system
group graphics drmrpc readproc
onrestart restart zygote
writepid /dev/stune/foreground/tasks
socket pdx/system/vr/display/client stream 0666 system graphics u:object_r:pdx_display_client_endpoint_socket:s0
socket pdx/system/vr/display/manager stream 0666 system graphics u:object_r:pdx_display_manager_endpoint_socket:s0
socket pdx/system/vr/display/vsync stream 0666 system graphics u:object_r:pdx_display_vsync_endpoint_socket:s0
我们直接来看一下 surfaceflinger 的main函数入口的相关。
- surfaceflinger::createSurfaceFlinger
- flinger->init()
- sm->addService(SurfaceFlinger::getServiceName())
- flinger->run()
// frameworks/native/services/surfaceflinger/main_surfaceflinger.cpp
int main(int, char**) {
signal(SIGPIPE, SIG_IGN);
hardware::configureRpcThreadpool(1 /* maxThreads */, false /* callerWillJoin */);
startGraphicsAllocatorService();
// 当 surfaceflinger 在自己的进程启动时,限制 binder 线程的数量
ProcessState::self()->setThreadPoolMaxThreadCount(4);
// 启动线程池
sp<ProcessState> ps(ProcessState::self());
ps->startThreadPool();
// 实例化 surfaceflinger
sp<SurfaceFlinger> flinger = surfaceflinger::createSurfaceFlinger();
setpriority(PRIO_PROCESS, 0, PRIORITY_URGENT_DISPLAY);
//...
// 在客户端可以连接前,进行一些SurfaceFlinger的初始化操作
flinger->init();
// 添加 SurfaceFlinger 服务到bincer
sp<IServiceManager> sm(defaultServiceManager());
sm->addService(String16(SurfaceFlinger::getServiceName()), flinger, false,
IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL | IServiceManager::DUMP_FLAG_PROTO);
startDisplayService(); // dependency on SF getting registered above
struct sched_param param = {
0};
param.sched_priority = 2;
if (sched_setscheduler(0, SCHED_FIFO, ¶m) != 0) {
ALOGE("Couldn't set SCHED_FIFO");
}
// 在当前线程运行 surface flinger
flinger->run();
return 0;
}
- SurfaceFlinger::init()
getFactory().createStartPropertySetThread
mStartPropertySetThread->Start()
// Do not call property_set on main thread which will be blocked by init
// Use StartPropertySetThread instead.
void SurfaceFlinger::init() {
ALOGI( "SurfaceFlinger's main thread ready to run. "
"Initializing graphics H/W...");
//...
// initialize our drawing state
mDrawingState = mCurrentState;
// set initial conditions (e.g. unblank default device)
initializeDisplays();
getRenderEngine().primeCache();
// 创建“属性设置线程”并启动,初始化graphics之后,mStartPropertySetThread() 会进行开机动画的播放
// 创建 StartPropertySetThread 实例,实际上SurfaceFlinger类里提供了创建实例的方法
const bool presentFenceReliable =
!getHwComposer().hasCapability(HWC2::Capability::PresentFenceIsNotReliable);
mStartPropertySetThread = getFactory().createStartPropertySetThread(presentFenceReliable);
if (mStartPropertySetThread->Start() != NO_ERROR) {
ALOGE("Run StartPropertySetThread failed!");
}
ALOGV("Done initializing");
}
我们再来看一下 StartPropertySetThread 这个线程做了一些什么操作。
StartPropertySetThread 类继承了 Thread 类,拥有线程的能力,在threadLoop函数中通过设置属性来控制开机动画的开始 。
// frameworks/native/services/surfaceflinger/StartPropertySetThread.cpp
//#include <utils/Thread.h>
namespace android {
StartPropertySetThread::StartPropertySetThread(bool timestampPropertyValue):
Thread(false), mTimestampPropertyValue(timestampPropertyValue) {
}
status_t StartPropertySetThread::Start() {
// 调用这里后,会回调到 threadLoop
// 这里使用了封装线程pthread_t的库,见system/core/libutils/include/utils/Thread.h,后面有机会会分析
return run("SurfaceFlinger::StartPropertySetThread", PRIORITY_NORMAL);
}
// 线程执行循环 threadLoop函数,根据返回值来判断是否继续循环, false 则退出循环
bool StartPropertySetThread::threadLoop() {
// Set property service.sf.present_timestamp, consumer need check its readiness
property_set(kTimestampProperty, mTimestampPropertyValue ? "1" : "0");
// 清除开机动画退出标志
property_set("service.bootanim.exit", "0");
// 通过设置属性来启动开机动画
property_set("ctl.start", "bootanim");
// 返回 false, 则立即退出线程
return false;
}
} // namespace android
为什么设置了属性后开机动画就能开始执行呢,原因是有地方监听了这个属性变化,在 init 进程启动的时候,system/core/init/main.cpp的main函数中启动了一个属性服务, 用于处理系统属性的变化。
# system/core/init/Android.bp
cc_binary {
name: "init_second_stage",
recovery_available: true,
stem: "init",
defaults: ["init_defaults"],
static_libs: ["libinit"],
required: [
"e2fsdroid",
"mke2fs",
"sload_f2fs",
"make_f2fs",
],
# main 函数入口
srcs: ["main.cpp"],
symlinks: ["ueventd"],
target: {
recovery: {
cflags: ["-DRECOVERY"],
exclude_shared_libs: ["libbinder", "libutils"],
},
},
ldflags: ["-Wl,--rpath,/system/${LIB}/bootstrap"],
}
init 进程 main 函数入口
// system/core/init/main.cpp
int main(int argc, char** argv) {
#if __has_feature(address_sanitizer)
__asan_set_error_report_callback(AsanReportCallback);
#endif
if (!strcmp(basename(argv[0]), "ueventd")) {
return ueventd_main(argc, argv);
}
if (argc > 1) {
if (!strcmp(argv[1], "subcontext")) {
android::base::InitLogging(argv, &android::base::KernelLogger);
const BuiltinFunctionMap function_map;
return SubcontextMain(argc, argv, &function_map);
}
if (!strcmp(argv[1], "selinux_setup")) {
// selinux_setup
return SetupSelinux(argv);
}
if (!strcmp(argv[1], "second_stage")) {
// second_stage 阶段,在这里启动了属性服务
return SecondStageMain(argc, argv);
}
}
// FirstStageMain 阶段,主要用于挂载系统基本的磁盘文件
return FirstStageMain(argc, argv);
}
- SecondStageMain
- property_init()
int SecondStageMain(int argc, char** argv) {
if (REBOOT_BOOTLOADER_ON_PANIC) {
InstallRebootSignalHandlers();
}
SetStdioToDevNull(argv);
// 初始化Kernel内核日志
InitKernelLogging(argv);
LOG(INFO) << "init second stage started!";
// 设置 init 进程及其子进程的 oom_adj -1000
if (auto result = WriteFile("/proc/1/oom_score_adj", "-1000"); !result) {
LOG(ERROR) << "Unable to write -1000 to /proc/1/oom_score_adj: " << result.error();
}
// 省略代码。。。
// Indicate that booting is in progress to background fw loaders, etc.
close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
// 属性初始化,从文件中解析系统属性,并存储到 property_infos 集合中,过滤集合中属性再将其写入 /dev/__properties__/property_info文件中
property_init();
// If arguments are passed both on the command line and in DT,
// properties set in DT always have priority over the command-line ones.
process_kernel_dt();
process_kernel_cmdline();
// Propagate the kernel variables to internal variables
// used by init as well as the current required properties.
export_kernel_boot_props();
// Make the time that init started available for bootstat to log.
property_set("ro.boottime.init", getenv("INIT_STARTED_AT"));
property_set("ro.boottime.init.selinux", getenv("INIT_SELINUX_TOOK"));
// ...
// 设置 SELinux
// Now set up SELinux for second stage.
SelinuxSetupKernelLogging();
SelabelInitialize();
SelinuxRestoreContext();
Epoll epoll;
if (auto result = epoll.Open(); !result)