Android bootanim流程分析
bootanim启动步骤
- init.rc 文件声明两个重要的服务 bootanim, surfaceflinger
- 启动界面依赖于 surface 管理服务,优先启动显示管理服务 surfaceflinger
- surfaceflinger 服务完成后,通过 (“ctl.start”, “bootanim”) 启动 bootanim 开机动画
当 system_server 启动系统 HomeUI 的时候关闭 bootanim
其具体流程可以参考下图所示
init.rc 文件相关声明
surfaceflinger类属core,随core类服务一同启动,服务重启直接导致Zygote服务的重启
service surfaceflinger /system/bin/surfaceflinger class core user system group graphics drmrpc system onrestart restart zygote
bootanim类属core,但是disable标志它不随core服务一同启动,用户及用户组均是graphics,而且仅启动一次oneshot
service bootanim /system/bin/bootanimation class core user graphics group graphics audio disabled oneshot
surfaceflinger服务的启动流程
surfaceflinger 的 main 方法
–> frameworks/native/services/surfaceflinger/main_surfaceflinger.cpp
int main(int, char**) { // 设置线程数量最大4 ProcessState::self()->setThreadPoolMaxThreadCount(4); // 开启线程池 sp<ProcessState> ps(ProcessState::self()); ps->startThreadPool(); // 实例surfaceflinger sp<SurfaceFlinger> flinger = new SurfaceFlinger(); setpriority(PRIO_PROCESS, 0, PRIORITY_URGENT_DISPLAY); set_sched_policy(0, SP_FOREGROUND); // 在客户端连接前初始化 flinger->init(); // 发布surfaceflinger就绪 sp<IServiceManager> sm(defaultServiceManager()); sm->addService(String16(SurfaceFlinger::getServiceName()), flinger, false); // 启动 flinger->run(); return 0; }
surfaceflinger 在 init 中启动 bootanim 服务
–> frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
surfaceflinger 的初始化工作代码众多,这里就不一一列举了,在其init过程中,启动了bootanim服务
void SurfaceFlinger::init() { // 初始化 EGL 用于显示 mEGLDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); eglInitialize(mEGLDisplay, NULL, NULL); mHwc = new HWComposer(this, *static_cast<HWComposer::EventHandler *>(this)); ... mDrawingState = mCurrentState; initializeDisplays(); // 启动 boot animation startBootAnim(); ... }
init 中通过 property_set(“ctl.start”, “bootanim”) 启动了 bootanim 服务
void SurfaceFlinger::startBootAnim() { // 开启 bootanim 服务 struct timespec t = {0, 0}; clock_gettime(CLOCK_MONOTONIC, &t); // 置位操作 property_set("service.bootanim.exit", "0"); property_set("ctl.start", "bootanim"); }
bootanim 服务的启动流程
总体概述
–> frameworks/base/cmds/bootanimation/BootAnimation.cpp
bootanim 流程如下图所示:
bootanim内部有大量的逻辑用于初始化和surfaceflinger通信,其内部依赖于安卓独有的binder通信机制,这里不做过多的说明。关于binder通信机制,需要大量篇幅的介绍说明,个人目前还没有这个能力。下面我们来看点可以看得懂的干货
readyToRun 流程
status_t BootAnimation::readyToRun() { ... // 创建 native层surface sp<SurfaceControl> control = session()->createSurface(String8("BootAnimation"), dinfo.w, dinfo.h, PIXEL_FORMAT_RGBA_8888, 0);
SurfaceComposerClient::openGlobalTransaction();
SurfaceComposerClient::closeGlobalTransaction();
sp<Surface> s = control->getSurface();
// 初始化 opengl 和 egl
EGLint w, h;
EGLint numConfigs;
EGLConfig config;
EGLSurface surface;
EGLContext context;
EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
eglInitialize(display, 0, 0);
eglChooseConfig(display, attribs, &config, 1, &numConfigs);
...
// 设备支持编码选项或者正处于编码进程中,显示编码动画
char decrypt[PROPERTY_VALUE_MAX];
property_get("vold.decrypt", decrypt, "");
bool encryptedAnimation = atoi(decrypt) != 0 || !strcmp("trigger_restart_min_framework", decrypt);
ZipFileRO* zipFile = NULL;
if ((encryptedAnimation &&
(access(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE, R_OK) == 0) &&
((zipFile = ZipFileRO::open(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE)) != NULL)) ||
((access(OEM_BOOTANIMATION_FILE, R_OK) == 0) &&
((zipFile = ZipFileRO::open(OEM_BOOTANIMATION_FILE)) != NULL)) ||
((access(SYSTEM_BOOTANIMATION_FILE, R_OK) == 0) &&
((zipFile = ZipFileRO::open(SYSTEM_BOOTANIMATION_FILE)) != NULL))) {
mZip = zipFile;
}
// 查看是否存在需要播放的video文件
if ((property_get("mstar.bootvideo", decrypt, NULL) > 0) ||
(access(USER_BOOTVIDEO_FILE, F_OK) == 0)) {
mVideo = true;
mZip = NULL;
} else {
mVideo = false;
if(property_get("mstar.close.bootlogo.gop.frame", decrypt, NULL) <= 0 ) {
property_set("mstar.close.bootlogo.gop.frame", "3");
}
}
return NO_ERROR;
}
这里需要注意mZip文件的获取,其三个文件的文件夹路径如下:
#define OEM_BOOTANIMATION_FILE "/oem/media/bootanimation.zip"
#define SYSTEM_BOOTANIMATION_FILE "/system/media/bootanimation.zip"
#define SYSTEM_ENCRYPTED_BOOTANIMATION_FILE "/system/media/bootanimation-encrypted.zip"
#define USER_BOOTVIDEO_FILE "/data/video/video.ts"
通过代码可以看出,文件的获取优先级是bootanimation-encrypted.zip最高,/oem/media/文件夹下的bootanimation.zip次之,最低级的是/system/media/文件夹下的bootanimation.zip。最后在检查指定目录是否存在用于启动播放的video视频。
threadLoop 流程
bool BootAnimation::threadLoop() { bool r; // 等待硬件初始化完成,开启 bootanim 动画 char property[PROPERTY_VALUE_MAX] = {0}; property_get("mstar.hw.init", property, "0"); int hwInit = atoi(property); memset(property, 0, PROPERTY_VALUE_MAX); property_get("mstar.str.suspending", property, "0"); int strSuspending = atoi(property); if (hwInit == 0) { int exitnow; property_get(EXIT_PROP_NAME, property, "0"); exitnow = atoi(property); if (exitnow) { r = false; goto exit; } return true; } // 存在video,播放它 // 存在bootanim文件,播放它 // 其他情况,显示静态logo if (mVideo && (strSuspending == 0)) { r = video(); } else if (mZip == NULL) { r = android(); } else { r = movie(); } exit: eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); eglDestroyContext(mDisplay, mContext); eglDestroySurface(mDisplay, mSurface); mFlingerSurface.clear(); mFlingerSurfaceControl.clear(); eglTerminate(mDisplay); IPCThreadState::self()->stopProcess(); return r; }
线程循环等待中的逻辑就更简单了。首先监测系统的硬件是否初始化完成,确保播放动画的相关配置均已ok,然后分别对上面获取的指定文件进行判断,优先播放video视频,其次是自定义的bootanim动画,如果什么都没有,就显示静态logo。
对于显示的具体流程这里就不做分析了,后续会对如何定制 bootanim.zip 文件进行集中的分析。这里我们只看一下代码显示logo中如何检测开机完成并关闭动画的逻辑。
动画显示关闭流程
bool BootAnimation::android()
{
// 初始化静态logo图片
initTexture(&mAndroid[0], mAssets, "images/android-logo-mask.png");
initTexture(&mAndroid[1], mAssets, "images/android-logo-shine.png");
// 清除屏幕
glShadeModel(GL_FLAT);
glDisable(GL_DITHER);
glDisable(GL_SCISSOR_TEST);
...
const nsecs_t startTime = systemTime();
do {
nsecs_t now = systemTime();
double time = now - startTime;
float t = 4.0f * float(time / us2ns(16667)) / mAndroid[1].w;
GLint offset = (1 - (t - floorf(t))) * mAndroid[1].w;
GLint x = xc - offset;
...
// 12fps: don't animate too fast to preserve CPU
const nsecs_t sleepTime = 83333 - ns2us(systemTime() - now);
if (sleepTime > 0)
usleep(sleepTime);
// 监测动画是否退出
checkExit();
} while (!exitPending());
glDeleteTextures(1, &mAndroid[0].name);
glDeleteTextures(1, &mAndroid[1].name);
return false;
}
显示静态logo中,死循环监测动画是否退出。
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();
if (mAudioPlayer != NULL) {
mAudioPlayer->requestExit();
}
}
}
#define EXIT_PROP_NAME "service.bootanim.exit"
checkExit中监测 value = "service.bootanim.exit" 的属性值,如果等于1,那么就要退出动画。
开机动画的关闭
概述
当SystemServer将系统中的关键服务启动完成后,会启动桌面启动器Launcher。Launcher启动后,会向ActivityManagerService发送一个Activity组件空闲通知,AMS收到该通知后,就会调用成员函数enableScreenAfterBoot()停止开机动画,以便让屏幕显示桌面。
AMS -> enableScreenAfterBoot
void enableScreenAfterBoot() { EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_ENABLE_SCREEN, SystemClock.uptimeMillis()); // 通知WMS enableScreenAfterBoot mWindowManager.enableScreenAfterBoot(); synchronized (this) { updateEventDispatchingLocked(); } }
WMS -> enableScreenAfterBoot
由AMS间接调用WMS的enableScreenAfterBoot通知开机完成。 public void enableScreenAfterBoot() { synchronized(mWindowMap) { ... mSystemBooted = true; hideBootMessagesLocked(); // If the screen still doesn't come up after 30 seconds, give // up and turn it on. mH.sendEmptyMessageDelayed(H.BOOT_TIMEOUT, 30*1000); } mPolicy.systemBooted(); // 系统启动完成,准备显示桌面 performEnableScreen(); }
准备桌面 -> performEnableScreen
public void performEnableScreen() { synchronized(mWindowMap) { ... if (!mBootAnimationStopped) { // Do this one time. try { // 通过binder通信机制,向SurfaceFlinger发送消息,停止开机动画 IBinder surfaceFlinger = ServiceManager.getService("SurfaceFlinger"); if (surfaceFlinger != null) { Parcel data = Parcel.obtain(); data.writeInterfaceToken("android.ui.ISurfaceComposer"); //消息内容为 IBinder.FIRST_CALL_TRANSACTION surfaceFlinger.transact(IBinder.FIRST_CALL_TRANSACTION, // BOOT_FINISHED data, null, 0); data.recycle(); } } catch (RemoteException ex) { Slog.e(TAG, "Boot completed: SurfaceFlinger is dead!"); } mBootAnimationStopped = true; } ... } }
代码可以看出,通过binder通信机制,获取到远程的surfaceflinger服务,向其发送 IBinder.FIRST_CALL_TRANSACTION 消息,远程服务接收到此消息再做处理。
surfaceflinger 接收消息处理
根据其传递的接口名”android.ui.ISurfaceComposer”,找到其文件位置,如下
–> frameworks/native/include/gui/ISurfaceComposer.h
–> frameworks/native/libs/gui/ISurfaceComposer.cpp其头文件又如下枚举定义值
class BnSurfaceComposer: public BnInterface<ISurfaceComposer> { public: enum { // Note: BOOT_FINISHED must remain this value, it is called from // Java by ActivityManagerService. BOOT_FINISHED = IBinder::FIRST_CALL_TRANSACTION, CREATE_CONNECTION, CREATE_GRAPHIC_BUFFER_ALLOC, CREATE_DISPLAY_EVENT_CONNECTION, ... } }
这里可以看到BOOT_FINISHED消息的定义,之后我们可以在 ISurfaceComposer.cpp 中找到处理这个消息的逻辑
ISurfaceComposer.cpp 处理 BOOT_FINISHED 消息
status_t BnSurfaceComposer::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { switch(code) { ... case BOOT_FINISHED: { CHECK_INTERFACE(ISurfaceComposer, data, reply); bootFinished(); return NO_ERROR; } ... } }
在 ISurfaceComposer.cpp 中的onTransact方法中,集中处理发来的binder消息,这里可以看出在对WMS发来的 BOOT_FINISHED(IBinder::FIRST_CALL_TRANSACTION) 消息,直接回掉bootFinished()方法。
virtual void bootFinished(){ Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); remote()->transact(BnSurfaceComposer::BOOT_FINISHED, data, &reply); }
SurfaceFlinger 回掉 bootFinished
SurfaceFlinger 继承 BnSurfaceComposer,BnSurfaceComposer 又继承 BnInterface,BnInterface 时定义好的模版,形参为 ISurfaceComposer。SurfaceFlinger的bootFinished方法如下:
void SurfaceFlinger::bootFinished(){ const nsecs_t now = systemTime(); const nsecs_t duration = now - mBootTime; mBootFinished = true; struct timespec t = {0, 0}; clock_gettime(CLOCK_MONOTONIC, &t); const String16 name("window"); sp<IBinder> window(defaultServiceManager()->getService(name)); if (window != 0) { window->linkToDeath(static_cast<IBinder::DeathRecipient*>(this)); } // 置位关闭动画属性值 property_set("service.bootanim.exit", "1"); }
走到这里,serfaceflinger就完成了关闭动画的属性值更改操作,后面就是bootanim程序死循环中不断检测指定属性值实现动画的关闭。
bootanim 循环监测实现关闭动画
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(); if (mAudioPlayer != NULL) { mAudioPlayer->requestExit(); } } }
总结
安卓开机动画的显示和消失,涉及的内容还是挺多的,想要详细了解其内部的实现流程还是有一定难度的。尤其是在binder消息的传递和处理上面,各个部分来回跳转,真的很头疼。对于bootanim.zip的自定义实现,后续会集中根据代码的解析来分析内部文件的具体含义。
引用参考博客中很好的一幅图片来收尾: