环境参数:
- android-14.0.0_r27
- Ubuntu 22.04.5 LTS
- aosp_cf_x86_64_phone-userdebug
在线源码网址:xrefandroid.com/
最近需要开发Android开机动画,所以想先来分析一下Android系统开机动画的流程。开机动画进程主要分为三个步骤:动画进程如何启动 、动画的绘制 以及动画进程如何结束。
开机动画主要涉及三个进程,先把其在AOSP源码中的位置列出来:
init: system/core/init
bootanim: framework/base/cmds/bootanimation
surfaceflinger: framework/native/services/surfacceflinger
Linux用户进程的鼻祖 - init进程
系统开机,Linux内核从无到有的第一个进程为swapper(pid=0),进而孵化Linux系统的用户进程 - Init进程(pid=1),它是所有用户进程的鼻祖。
- init进程能孵化出ueventd,adbd,logd,installd等用户守护进程
- init进程能启动servicemanager(Binder管家)、bootanim等重要服务
- init进程能孵化出Zygote进程
Init进程的从/system/core/init/main.cpp
的main()
函数开始,调用/system/core/init/init.cpp
的SecondStageMain()
函数,最终会执行LoadBootScripts()
函数:
scss
// @/system/core/init/init.cpp
static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) {
Parser parser = CreateParser(action_manager, service_list);
std::string bootscript = GetProperty("ro.boot.init_rc", "");
if (bootscript.empty()) {
parser.ParseConfig("/system/etc/init/hw/init.rc");
if (!parser.ParseConfig("/system/etc/init")) {
late_import_paths.emplace_back("/system/etc/init");
}
// late_import is available only in Q and earlier release. As we don't
// have system_ext in those versions, skip late_import for system_ext.
parser.ParseConfig("/system_ext/etc/init");
if (!parser.ParseConfig("/vendor/etc/init")) {
late_import_paths.emplace_back("/vendor/etc/init");
}
if (!parser.ParseConfig("/odm/etc/init")) {
late_import_paths.emplace_back("/odm/etc/init");
}
if (!parser.ParseConfig("/product/etc/init")) {
late_import_paths.emplace_back("/product/etc/init");
}
} else {
parser.ParseConfig(bootscript);
}
}
init进程会去解析各种rc文件,包括/system/etc/init/hw/init.rc
文件和system/etc/init
目录下的rc文件
adb查看模拟器system/etc/init
文件夹中的文件:
其中包含surfaceflinger.rc和bootanim.rc文件
开机动画绘制流程
先看bootanim.rc文件:
python
#/frameworks/base/cmds/bootanimation/bootanim.rc
service bootanim /system/bin/bootanimation
class core animation
user graphics
group graphics audio
disabled
oneshot
ioprio rt 0
task_profiles MaxPerformance
系统会去启动/system/bin/
目录下的bootanimation服务(进程),但是因为下面service的设置为disabled,表示此服务不会自动启动,而是需要显示调用服务名来启动。如何启动,后面会提到。
但此bootanimation服务来源于哪呢?
再查看bootanimation的bp编译配置文件frameworks/base/cmds/bootanimation/Android.bp
arduino
# frameworks/base/cmds/bootanimation/Android.bp
cc_binary {
name: "bootanimation",
defaults: ["bootanimation_defaults"],
header_libs: ["jni_headers"],
shared_libs: [
"libOpenSLES",
"libbootanimation",
],
srcs: [
"BootAnimationUtil.cpp",
"bootanimation_main.cpp",
"audioplay.cpp",
],
init_rc: ["bootanim.rc"],
cflags: [
"-Wno-deprecated-declarations",
],
}
此文件定义了一个可执行文件bootanimation, 也就是上面所说的bootanimation服务,入口文件为bootanimation_main.cpp
, init_rc
文件指定为当前目录的bootanim.rc
文件,编译时,系统会将bootanimation执行文件放在/system/bin/
目录下
bootanimation服务启动后会先进入bootanimation_main.cpp
的main
函数
scss
int main()
{
setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_DISPLAY);
bool noBootAnimation = bootAnimationDisabled();
ALOGI_IF(noBootAnimation, "boot animation disabled");
if (!noBootAnimation) {
sp<ProcessState> proc(ProcessState::self());
ProcessState::self()->startThreadPool();
// create the boot animation object (may take up to 200ms for 2MB zip)
sp<BootAnimation> boot = new BootAnimation(audioplay::createAnimationCallbacks());
waitForSurfaceFlinger();
boot->run("BootAnimation", PRIORITY_DISPLAY);
ALOGV("Boot animation set up. Joining pool.");
IPCThreadState::self()->joinThreadPool();
}
return 0;
}
通过bootAnimationDisabled()
函数判断系统是否跳过开机动画。需要显示开机动画时,先去创建一个BootAnimation
线程实例,同时需要等待surfaceflinger启动完成后,调用run方法启动线程
上面代码中boot变量为sp类型,为sp强智能指针,其在初始化的时候会触发onFirstRef()
方法调用
scss
#/frameworks/base/cmds/bootanimation/BootAnimation.cpp#onFirstRef
void BootAnimation::onFirstRef() {
status_t err = mSession->linkToComposerDeath(this);
SLOGE_IF(err, "linkToComposerDeath failed (%s) ", strerror(-err));
if (err == NO_ERROR) {
// Load the animation content -- this can be slow (eg 200ms)
// called before waitForSurfaceFlinger() in main() to avoid wait
ALOGD("%sAnimationPreloadTiming start time: %" PRId64 "ms",
mShuttingDown ? "Shutdown" : "Boot", elapsedRealtime());
preloadAnimation();
ALOGD("%sAnimationPreloadStopTiming start time: %" PRId64 "ms",
mShuttingDown ? "Shutdown" : "Boot", elapsedRealtime());
}
}
preloadAnimation方法会去预加载动画资源。
因为BootAnimation继承于Thread,当线程被执行run()
函数时,Thread会先调用readyToRun()
方法做一些初始化工作
ini
#/frameworks/base/cmds/bootanimation/BootAnimation.cpp#readyToRun
status_t BootAnimation::TimeCheckThread::readyToRun() {
mInotifyFd = inotify_init();
if (mInotifyFd < 0) {
SLOGE("Could not initialize inotify fd");
return NO_INIT;
}
mBootAnimWd = inotify_add_watch(mInotifyFd, BOOTANIM_DATA_DIR_PATH, IN_CREATE | IN_ATTRIB);
if (mBootAnimWd < 0) {
close(mInotifyFd);
mInotifyFd = -1;
SLOGE("Could not add watch for %s: %s", BOOTANIM_DATA_DIR_PATH, strerror(errno));
return NO_INIT;
}
addTimeDirWatch();
if (mBootAnimation->updateIsTimeAccurate()) {
close(mInotifyFd);
mInotifyFd = -1;
return ALREADY_EXISTS;
}
return NO_ERROR;
}
在调用readyToRun()
方法后,线程再执行threadLoop()
方法:
scss
#/frameworks/base/cmds/bootanimation/BootAnimation.cpp#threadLoop
bool BootAnimation::threadLoop() {
bool result;
initShaders();
// We have no bootanimation file, so we use the stock android logo
// animation.
if (mZipFileName.isEmpty()) {
ALOGD("No animation file");
result = android();
} else {
result = movie();
}
mCallbacks->shutdown();
eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
eglDestroyContext(mDisplay, mContext);
eglDestroySurface(mDisplay, mSurface);
mFlingerSurface.clear();
mFlingerSurfaceControl.clear();
eglTerminate(mDisplay);
eglReleaseThread();
IPCThreadState::self()->stopProcess();
return result;
}
当配置了动画资源,会执行movie()
方法进行播放,当没有配置动画资源,会执行android()
方法去播放原生的logo图片
在movie()
方法中,会去加载动画,播放动画和释放动画。
ini
#/frameworks/base/cmds/bootanimation/BootAnimation.cpp#movie
bool BootAnimation::movie() {
if (mAnimation == nullptr) {
mAnimation = loadAnimation(mZipFileName);
}
//opengl
...
playAnimation(*mAnimation);
...
releaseAnimation(mAnimation);
return false;
}
在播放动画函数playAnimation()
中,会通过循环一帧一帧的展示图片,同时每一帧都会通过checkExit()
监测是否可以退出显示了
ini
for (int r=0 ; !part.count || r<part.count || fadedFramesCount > 0 ; r++) {
for (int r=0 ; !part.count || r<part.count ; r++) {
...
for (size_t j=0 ; j<fcount && (!exitPending() || part.playUntilComplete) ; j++) {
...
checkExit();
}
}
...
return true;
}
在android()
方法中,会先去初始化需要显示的图片纹理,然后在一个循环中去绘制出来,同时每次都会通过checkExit()
监测是否可以退出显示了
ini
bool BootAnimation::android() {
...
initTexture(&mAndroid[0], mAssets, "images/android-logo-mask.png");
initTexture(&mAndroid[1], mAssets, "images/android-logo-shine.png");
...
do {
...
drawTexturedQuad(xc, yc, mAndroid[0].w, mAndroid[0].h);
...
checkExit();
} while (!exitPending());
return false;
}
监测退出函数checkExit()
通过获取service.bootanim.exit属性值,来判断是否退出播放线程
scss
static const char EXIT_PROP_NAME[] = "service.bootanim.exit";
void BootAnimation::checkExit() {
// Allow surface flinger to gracefully request shutdown
char value[PROPERTY_VALUE_MAX];
property_get(EXIT_PROP_NAME, value, "0");
int exitnow = atoi(value);
if (exitnow) {
requestExit();
}
}
因此,init进程会去加载各种rc文件,启动对应的各种服务,因为bootanim.rc的service设置为disabled而不会在init阶段去启动bootanimation服务(进程);而当bootanimation启动时,会先在创建的线程运行之前去加载配置的资源文件,从而在线程运行时显示对应的资源动画,同时在显示动画的时候去检查是否需要退出动画显示。
如何去启动开机动画
那bootanimation是如何被启动的呢?开机动画需要屏幕绘制,屏幕绘制都与SurfaceFlinger相关,先分析一下上文剩余的另一个rc文件surfaceflinger.rc
perl
service surfaceflinger /system/bin/surfaceflinger
class core animation
user system
group graphics drmrpc readproc
capabilities SYS_NICE
onrestart restart --only-if-running zygote
task_profiles HighPerformance
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
在init阶段,init进程会去启动位于/system/bin/下的surfaceflinger服务
surfaceflinger服务在/frameworks/native/services/surfaceflinger/Android.bp文件下定义
arduino
// @frameworks/native/services/surfaceflinger/Android.bp
filegroup {
name: "surfaceflinger_binary_sources",
srcs: [
":libsurfaceflinger_sources",
"main_surfaceflinger.cpp",
],
}
cc_binary {
name: "surfaceflinger",
defaults: ["libsurfaceflinger_binary"],
init_rc: ["surfaceflinger.rc"],
srcs: [
":surfaceflinger_binary_sources",
// Note: SurfaceFlingerFactory is not in the filegroup so that it
// can be easily replaced.
"SurfaceFlingerFactory.cpp",
],
shared_libs: [
"libSurfaceFlingerProp",
],
logtags: ["EventLog/EventLogTags.logtags"],
}
定义了一个可执行文件surfaceflinger
,可执行文件surfaceflinger
的init_rc
文件指定为当前目录的surfaceflinger.rc
文件,在编译的时候系统会将surfaceflinger.rc
文件安装到system/etc/init
, surfaceflinger的入口文件为main_surfaceflinger.cpp
scss
// @frameworks/native/services/surfaceflinger/main_surfaceflinger.cpp
int main(int char**){
...
// instantiate surfaceflinger
sp<SurfaceFlinger> flinger = surfaceflinger::createSurfaceFlinger();
...
// initialize before clients can connect
flinger->init();
// publish surface flinger
sp<IServiceManager> sm(defaultServiceManager());
sm->addService(String16(SurfaceFlinger::getServiceName()), flinger, false,
...
// run surface flinger in this thread
flinger->run();
...
}
主要是创建了一个surfaceflinger实例,然后先后调用了surfaceflinger的init方法和run方法:
再来看surfaceflinger中的init方法:主要是开启一个线程,类型为StartPropertySetThread
.h文件声明变量:
java
@frameworks/native/services/surfaceflinger/surfaceflinger.h
sp<StartPropertySetThread> mStartPropertySetThread;
scss
@frameworks/native/services/surfaceflinger/surfaceflinger.cpp
void SurfaceFlinger::init() FTL_FAKE_GUARD(kMainThreadContext) {
...
mStartPropertySetThread = getFactory().createStartPropertySetThread(presentFenceReliable);
if (mStartPropertySetThread->Start() != NO_ERROR) {
ALOGE("Run StartPropertySetThread failed!");
}
...
}
线程运行时会执行到threadLoop方法:
less
@frameworks/native/services/surfaceflinger/StartPropertySetThread.cpp#threadLoop
bool StartPropertySetThread::threadLoop() {
// Set property service.sf.present_timestamp, consumer need check its readiness
property_set(kTimestampProperty, mTimestampPropertyValue ? "1" : "0");
// Clear BootAnimation exit flag
property_set("service.bootanim.exit", "0");
property_set("service.bootanim.progress", "0");
// Start BootAnimation if not started
property_set("ctl.start", "bootanim");
// Exit immediately
return false;
}
首先会设置service.bootanim.exit属性为0,同时将ctl.start属性设置为bootanim,表示需要启动bootanim。 ctl.start属性值改变时,init进程就会收到这个变化事件,init进程就会去启动bootanimation服务。
因此init进程在init阶段会去启动surfaceflinger进程,surfaceflinger启动的时候会先创建一个SurfaeFlinger对象,在其init()函数中通过创建StartPropertySetThread线程去修改系统属性ctl.start,通知init进程去启动bootanimation进程。具体为什么修改了系统属性就能通知init进程去启动bootanimation,可参看另外一篇文章:待定~
什么时候结束开机动画
bootanimation如何启动的已经分析,那bootanimation如何结束呢?上文提到checkExit()函数,bootanimation服务通过不断获取系统属性service.bootanim.exit的值来判断是否需要结束动画。
framework源码全局搜索系统哪些地方对此属性赋值: 发现有两处对service.bootanim.exit设置为1,表示退出开机动画。
先来看SurfaceFlinger.cpp中是如何发起关闭动画行为的:
typescript
@frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
void SurfaceFlinger::bootFinished() {
if (mBootFinished == true) {
ALOGE("Extra call to bootFinished");
return;
}
mBootFinished = true;
...
// stop boot animation
// formerly we would just kill the process, but we now ask it to exit so it
// can choose where to stop the animation.
property_set("service.bootanim.exit", "1");
...
}
在SurfaceFlinger.cpp的bootFinished()
函数中修改service.bootanim.exit
属性,bootanimation服务会不断获取此属性的值从而达到关闭开机动画的目的,而SurfaceFlinger::bootFinished()
函数会在SurfaceComposerAIDL::bootFinished()
函数中被调用:
scss
@frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
// gui::ISurfaceComposer
binder::Status SurfaceComposerAIDL::bootFinished() {
status_t status = checkAccessPermission();
if (status != OK) {
return binderStatusFromStatusT(status);
}
mFlinger->bootFinished();
return binder::Status::ok();
}
SurfaceComposerAIDL::bootFinished()
函数是在SurfaceComposerAIDL类中声明的函数:
css
@frameworks/native/services/surfaceflinger/SurfaceFlinger.h
class SurfaceComposerAIDL : public gui::BnSurfaceComposer {
public:
SurfaceComposerAIDL(sp<SurfaceFlinger> sf) : mFlinger(std::move(sf)) {}
binder::Status bootFinished() override;
}
通过在framework文件夹中搜索关键字bootFinished的结果来看:
SurfaceComposerAIDL::bootFinished()
函数是在SurfaceComposerClient.cpp中被调用的:
scss
@frameworks/native/libs/gui/SurfaceComposerClient.cpp
status_t SurfaceComposerClient::bootFinished() {
sp<gui::ISurfaceComposer> sf(ComposerServiceAIDL::getComposerService());
binder::Status status = sf->bootFinished();
return statusTFromBinderStatus(status);
}
SurfaceComposerClient::bootFinished()
函数在android_view_SurfaceControl.cpp的nativeBootFinished函数被调用:
ini
@frameworks/base/core/jni/android_view_SurfaceControl.cpp
static jboolean nativeBootFinished(JNIEnv* env, jclass clazz) {
status_t error = SurfaceComposerClient::bootFinished();
return error == OK ? JNI_TRUE : JNI_FALSE;
}
继续往上则是从SurfaceControl.java的bootFinished()方法,通过JNI与原生方法nativeBootFinished()交互:
java
@frameworks/base/core/java/android/view/SurfaceControl.java
public static boolean bootFinished() {
return nativeBootFinished();
}
private static native boolean nativeBootFinished();
SurfaceControl.java的静态方法bootFinished()明显是从WindowManagerService.java中调用起来的:
csharp
private void performEnableScreen() {
synchronized (mGlobalLock) {
...
if (!mBootAnimationStopped) {
Trace.asyncTraceBegin(TRACE_TAG_WINDOW_MANAGER, "Stop bootanim", 0);
// stop boot animation
// formerly we would just kill the process, but we now ask it to exit so it
// can choose where to stop the animation.
SystemProperties.set("service.bootanim.exit", "1");
mBootAnimationStopped = true;
}
...
if (!SurfaceControl.bootFinished()) {
ProtoLog.w(WM_ERROR, "performEnableScreen: bootFinished() failed.");
return;
}
...
}
}
发现在调用SurfaceControl.bootFinished()
之前,还会通过SystemProperties.set("service.bootanim.exit", "1")
设置系统属性来通知系统关闭开机动画
因此目前观察到开机动画的停止动作发起点在WMS:
- 先通过设置service.bootanim.exit系统属性为1,bootanimation服务检测到后则关闭开机动画
- 而后再次判断开机动画是否结束,如果没有,则通过静态函数,JNI接口等一系列操作,最后在SurfaceFlinger中,设置service.bootanim.exit系统属性为1来关闭开机动画
再深挖performEnableScreen方法前后调用的堆栈信息:
具体分析另起文章。