首语
在Android设备开机启动时,会展示Android开机动画,用于增加用户体验和展示设备品牌等信息。它也是Android系统启动的一部分。开机动画是由bootanimation负责的,因此首先先了解下bootanimation是如何启动的。
bootanimation 启动脚本分析
init进程中第二阶段(SecondStageMain)的主要工作有初始化属性服务,加载启动脚本,解析init.rc文件等。
源码路径:system/core/init/init.cpp
intSecondStageMain(int argc,char** argv){...LoadBootScripts(am, sm);...}staticvoidLoadBootScripts(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文件中,可以看到通过class_start来启动 classname 为 core 的 Service。在bootanimation.rc文件中,可以清楚看到Service name为bootanim,执行程序路径为:/system/bin/bootanimation,类名:core。disabled表示系统启动时,不会自动启动bootanimation。那是谁启动bootanimation呢?
SurfaceFlinger它负责管理图形内容的渲染,并将多个图层(包括应用程序窗口、系统UI元素和硬件覆盖层)合成到设备的屏幕上。所以首先需要启动SurfaceFlinger,开机动画的渲染和合成是它完成的,继续分析SurfaceFlinger启动流程。
源码路径:system/core/rootdir/init.rc
...
# Start standard binderized HAL daemons
class_start hal
class_start core
...
源码路径: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
SurfaceFlinger启动流程
而surfaceflinger.rc文件中,可以清楚看到Service name为surfaceflinger,执行程序路径为:/system/bin/surfaceflinger,类名:core。
因此,可以知道SurfaceFlinger是在init进程启动第二阶段进行启动的。
源码路径:frameworks/native/services/surfaceflinger/surfaceflinger.rc
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
看下SurfaceFlinger的main函数,创建了SurfaceFlinger并且初始化,调用StartPropertySetThread的Start函数。
源码路径:frameworks/native/services/surfaceflinger/main_surfaceflinger.cpp
intmain(int,char**){...// instantiate surfaceflinger
sp<SurfaceFlinger> flinger = surfaceflinger::createSurfaceFlinger();...// initialize before clients can connect
flinger->init();...}
源码路径:frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
voidSurfaceFlinger::init(){...
mStartPropertySetThread =getFactory().createStartPropertySetThread(presentFenceReliable);if(mStartPropertySetThread->Start()!= NO_ERROR){ALOGE("Run StartPropertySetThread failed!");}...}
可以看到将系统属性service.bootanim.exit/service.bootanim.progress设置为0,并将ctl.start设置为bootanim,当系统属性发生改变时,init进程就会接收到一个系统属性变化通知,这个通知最终是由在init进程中的函数handle_property_set_fd来处理的。设置ctl.start表示启动一个服务,这样bootanimation就被启动了。
源码路径:frameworks/native/services/surfaceflinger/StartPropertySetThread.cpp
status_t StartPropertySetThread::Start(){returnrun("SurfaceFlinger::StartPropertySetThread", PRIORITY_NORMAL);}boolStartPropertySetThread::threadLoop(){// Set property service.sf.present_timestamp, consumer need check its readinessproperty_set(kTimestampProperty, mTimestampPropertyValue ?"1":"0");// Clear BootAnimation exit flagproperty_set("service.bootanim.exit","0");property_set("service.bootanim.progress","0");// Start BootAnimation if not startedproperty_set("ctl.start","bootanim");// Exit immediatelyreturnfalse;}
bootanimation启动流程
分析bootanimation的main函数,首先判断是否禁用了启动动画,没有则创建一个binder线程池,再创建BootAnimation,等待SurfaceFlinger启动完成。
源码路径:frameworks/base/cmds/bootanimation/bootanimation_main.cpp
intmain(){setpriority(PRIO_PROCESS,0, ANDROID_PRIORITY_DISPLAY);//是否禁用了启动动画bool noBootAnimation =bootAnimationDisabled();ALOGI_IF(noBootAnimation,"boot animation disabled");if(!noBootAnimation){//创建binder线程池
sp<ProcessState>proc(ProcessState::self());ProcessState::self()->startThreadPool();// create the boot animation object (may take up to 200ms for 2MB zip)
sp<BootAnimation> boot =newBootAnimation(audioplay::createAnimationCallbacks());waitForSurfaceFlinger();
boot->run("BootAnimation", PRIORITY_DISPLAY);ALOGV("Boot animation set up. Joining pool.");IPCThreadState::self()->joinThreadPool();}return0;}
源码路径:frameworks/base/cmds/bootanimation/BootAnimationUtil.cpp
boolbootAnimationDisabled(){char value[PROPERTY_VALUE_MAX];//启动动画调试模式property_get("debug.sf.nobootanimation", value,"0");if(atoi(value)>0){returntrue;}property_get("ro.boot.quiescent", value,"0");if(atoi(value)>0){// Only show the bootanimation for quiescent boots if this system property is set to enabled//禁用启动动画if(!property_get_bool("ro.bootanim.quiescent.enabled",false)){returntrue;}}returnfalse;}voidwaitForSurfaceFlinger(){// TODO: replace this with better waiting logic in future, b/35253872int64_t waitStartTime =elapsedRealtime();
sp<IServiceManager> sm =defaultServiceManager();const String16 name("SurfaceFlinger");constint SERVICE_WAIT_SLEEP_MS =100;constint LOG_PER_RETRIES =10;int retry =0;while(sm->checkService(name)==nullptr){
retry++;if((retry % LOG_PER_RETRIES)==0){ALOGW("Waiting for SurfaceFlinger, waited for %" PRId64 " ms",elapsedRealtime()- waitStartTime);}usleep(SERVICE_WAIT_SLEEP_MS *1000);};int64_t totalWaited =elapsedRealtime()- waitStartTime;if(totalWaited > SERVICE_WAIT_SLEEP_MS){ALOGI("Waiting for SurfaceFlinger took %" PRId64 " ms", totalWaited);}}
BootAnimation类有几个重要函数
- onFirstRef(),属于父类RefBase,它用于实现引用计数的对象管理,新增引用计数时调用。
- binderDied() ,Binder结束时,就会回调binderDied()方法。
- readyToRun() ,Thread执行前的初始化工作。
- threadLoop() ,线程根据逻辑是否循环执行。
- android(),显示系统默认的开机画面。
- movie(),显示用户自定义的开机动画。
- loadAnimation(),加载动画。
- playAnimation(),播放动画。
- checkExit(),检查是否退出动画。
源码路径:frameworks/base/cmds/bootanimation/BootAnimation.h
private:virtualboolthreadLoop();virtual status_t readyToRun();virtualvoidonFirstRef();virtualvoidbinderDied(const wp<IBinder>& who);...//系统默认的开机画面boolandroid();//用户自定义的开机动画boolmovie();...//加载动画
Animation*loadAnimation(const String8&);//播放动画boolplayAnimation(const Animation&);voidreleaseAnimation(Animation*)const;boolparseAnimationDesc(Animation&);boolpreloadZip(Animation &animation);voidfindBootAnimationFile();boolfindBootAnimationFileInternal(const std::vector<std::string>& files);boolpreloadAnimation();
EGLConfig getEglConfig(const EGLDisplay&);
ui::Size limitSurfaceSize(int width,int height)const;voidresizeSurface(int newWidth,int newHeight);voidprojectSceneToWindow();boolshouldStopPlayingPart(const Animation::Part& part,int fadedFramesCount,int lastDisplayedProgress);//检查是否退出动画voidcheckExit();
BootAnimation构造函数中,创建了SurfaceComposerClient,mSession用来和SurfaceFlinger执行Binder进程间通信,执行linkToComposerDeath方法用于获取SurfaceFlinger死亡通知,preloadAnimation方法开始加载动画,首先去查询动画文件,动画文件的存放位置如代码中定义所示。动画文件是按照指定位置顺序读取,如果读取到当前位置动画文件,则不读取后续动画文件。
源码路径:frameworks/base/cmds/bootanimation/BootAnimation.cpp
staticconstchar OEM_BOOTANIMATION_FILE[]="/oem/media/bootanimation.zip";staticconstchar PRODUCT_BOOTANIMATION_DARK_FILE[]="/product/media/bootanimation-dark.zip";staticconstchar PRODUCT_BOOTANIMATION_FILE[]="/product/media/bootanimation.zip";staticconstchar SYSTEM_BOOTANIMATION_FILE[]="/system/media/bootanimation.zip";staticconstchar APEX_BOOTANIMATION_FILE[]="/apex/com.android.bootanimation/etc/bootanimation.zip";staticconstchar PRODUCT_ENCRYPTED_BOOTANIMATION_FILE[]="/product/media/bootanimation-encrypted.zip";staticconstchar SYSTEM_ENCRYPTED_BOOTANIMATION_FILE[]="/system/media/bootanimation-encrypted.zip";staticconstchar OEM_SHUTDOWNANIMATION_FILE[]="/oem/media/shutdownanimation.zip";staticconstchar PRODUCT_SHUTDOWNANIMATION_FILE[]="/product/media/shutdownanimation.zip";staticconstchar SYSTEM_SHUTDOWNANIMATION_FILE[]="/system/media/shutdownanimation.zip";staticconstexprconstchar* PRODUCT_USERSPACE_REBOOT_ANIMATION_FILE ="/product/media/userspace-reboot.zip";staticconstexprconstchar* OEM_USERSPACE_REBOOT_ANIMATION_FILE ="/oem/media/userspace-reboot.zip";staticconstexprconstchar* SYSTEM_USERSPACE_REBOOT_ANIMATION_FILE ="/system/media/userspace-reboot.zip";BootAnimation::BootAnimation(sp<Callbacks> callbacks):Thread(false),mLooper(newLooper(false)),mClockEnabled(true),mTimeIsAccurate(false),mTimeFormat12Hour(false),mTimeCheckThread(nullptr),mCallbacks(callbacks){
mSession =newSurfaceComposerClient();
std::string powerCtl = android::base::GetProperty("sys.powerctl","");if(powerCtl.empty()){
mShuttingDown =false;}else{
mShuttingDown =true;}ALOGD("%sAnimationStartTiming start time: %" PRId64 "ms", mShuttingDown ?"Shutdown":"Boot",elapsedRealtime());}voidBootAnimation::onFirstRef(){//接收SurfaceFlinger死亡通知
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 waitALOGD("%sAnimationPreloadTiming start time: %" PRId64 "ms",
mShuttingDown ?"Shutdown":"Boot",elapsedRealtime());preloadAnimation();ALOGD("%sAnimationPreloadStopTiming start time: %" PRId64 "ms",
mShuttingDown ?"Shutdown":"Boot",elapsedRealtime());}}boolBootAnimation::preloadAnimation(){findBootAnimationFile();if(!mZipFileName.isEmpty()){//加载动画
mAnimation =loadAnimation(mZipFileName);return(mAnimation !=nullptr);}returnfalse;}voidBootAnimation::findBootAnimationFile(){// If the device has encryption turned on or is in process// of being encrypted we show the encrypted boot animation.char decrypt[PROPERTY_VALUE_MAX];property_get("vold.decrypt", decrypt,"");bool encryptedAnimation =atoi(decrypt)!=0||!strcmp("trigger_restart_min_framework", decrypt);if(!mShuttingDown && encryptedAnimation){staticconst std::vector<std::string> encryptedBootFiles ={
PRODUCT_ENCRYPTED_BOOTANIMATION_FILE, SYSTEM_ENCRYPTED_BOOTANIMATION_FILE,};if(findBootAnimationFileInternal(encryptedBootFiles)){return;}}constbool playDarkAnim = android::base::GetIntProperty("ro.boot.theme",0)==1;staticconst std::vector<std::string> bootFiles ={
APEX_BOOTANIMATION_FILE, playDarkAnim ? PRODUCT_BOOTANIMATION_DARK_FILE : PRODUCT_BOOTANIMATION_FILE,
OEM_BOOTANIMATION_FILE, SYSTEM_BOOTANIMATION_FILE
};staticconst std::vector<std::string> shutdownFiles ={
PRODUCT_SHUTDOWNANIMATION_FILE, OEM_SHUTDOWNANIMATION_FILE, SYSTEM_SHUTDOWNANIMATION_FILE,""};staticconst std::vector<std::string> userspaceRebootFiles ={
PRODUCT_USERSPACE_REBOOT_ANIMATION_FILE, OEM_USERSPACE_REBOOT_ANIMATION_FILE,
SYSTEM_USERSPACE_REBOOT_ANIMATION_FILE,};if(android::base::GetBoolProperty("sys.init.userspace_reboot.in_progress",false)){findBootAnimationFileInternal(userspaceRebootFiles);}elseif(mShuttingDown){findBootAnimationFileInternal(shutdownFiles);}else{findBootAnimationFileInternal(bootFiles);}}boolBootAnimation::findBootAnimationFileInternal(const std::vector<std::string>&files){for(const std::string& f : files){if(access(f.c_str(), R_OK)==0){
mZipFileName = f.c_str();returntrue;}}returnfalse;}
BootAnimation::Animation*BootAnimation::loadAnimation(const String8& fn){if(mLoadedFiles.indexOf(fn)>=0){SLOGE("File \"%s\" is already loaded. Cyclic ref is not allowed",
fn.string());returnnullptr;}
ZipFileRO *zip =ZipFileRO::open(fn);if(zip ==nullptr){SLOGE("Failed to open animation zip \"%s\": %s",
fn.string(),strerror(errno));returnnullptr;}ALOGD("%s is loaded successfully", fn.string());
Animation *animation =new Animation;
animation->fileName = fn;
animation->zip = zip;
animation->clockFont.map =nullptr;
mLoadedFiles.add(animation->fileName);//解析动画文件parseAnimationDesc(*animation);if(!preloadZip(*animation)){releaseAnimation(animation);returnnullptr;}
mLoadedFiles.remove(fn);return animation;}boolBootAnimation::parseAnimationDesc(Animation& animation){
String8 desString;if(!readFile(animation.zip,"desc.txt", desString)){returnfalse;}charconst* s = desString.string();
std::string dynamicColoringPartName ="";bool postDynamicColoring =false;// Parse the description file...}
bootanimation.zip
动画文件的压缩包里都存在一个动画配置文件desc.txt,它是描述开机动画是如何显示的。我们以device/google/atv/products/bootanimations/bootanimation.zip动画压缩包为例进行分析,它是AndroidTV存储动画文件的路径。desc.txt内容如下:
第1行用来描述开机动画在屏幕显示的大小及帧率。这个定义指示 bootanimation 的播放分辨率为 512x416 像素,帧率为 60 帧/秒。分辨率定义了动画的宽度和高度,而帧率定义了动画播放的流畅程度,即每秒播放的帧数。
第2行c:表示清除命令。1:表示清除的起始帧。0:表示清除的结束帧。part0:表示需要清除的动画帧所在的文件夹路径。这个定义指示在播放动画时,从指定的文件夹 part0 中清除第 1 帧。这样可以控制在播放过程中是否清除特定的帧,以实现动画效果的变化或平滑的过渡效果。3-5行同理。
最后一行f:表示循环命令。0:表示循环的起始帧。0:表示循环的结束帧。part4:表示需要循环的动画帧所在的文件夹路径。10:表示循环次数。该行指示在播放动画时,从指定的文件夹中的起始帧到结束帧之间的帧进行循环播放,重复播放 10 次。
512 416 60
c 1 0 part0
c 1 15 part1
c 1 0 part2
c 1 0 part3
f 0 0 part4 10
动画配置文件还有指定播放顺序的,例如如下的配置,p:表示播放顺序命令。1:表示播放的顺序。0:表示播放的循环次数。0 表示无限循环。folder1:表示动画帧所在的文件夹路径。根据这个定义,folder1 是一个目录,包含了一组 bootanimation 动画帧文件。该行指示在播放动画时,按照顺序从 folder1 目录中加载帧并进行播放。
p 1 0 folder1
加载动画执行完成后,接下来会执行主体函数threadLoop,首先判断自定义开机动画文件是否存在,如果不存在则执行Android方法,否则执行自定义动画Movie方法。Android和Movie方法最后都返回false。因此threadloop也返回false,代表代码只执行一次。最后获取service.bootanim.exit属性,如果值为1,循环就会退出,开机动画就会结束。service.bootanim.exit属性是在AMS中被修改为1的,在后面AMS中会讲到。
staticconstchar EXIT_PROP_NAME[]="service.bootanim.exit";boolBootAnimation::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;}boolBootAnimation::android(){glActiveTexture(GL_TEXTURE0);SLOGD("%sAnimationShownTiming start time: %" PRId64 "ms", mShuttingDown ?"Shutdown":"Boot",elapsedRealtime());//android-logo-mask.png和android-logo-shine.png保存在frameworks/base/core/res/assets/images/路径下initTexture(&mAndroid[0], mAssets,"images/android-logo-mask.png");initTexture(&mAndroid[1], mAssets,"images/android-logo-shine.png");
mCallbacks->init({});...returnfalse;}boolBootAnimation::movie(){if(mAnimation ==nullptr){
mAnimation =loadAnimation(mZipFileName);}if(mAnimation ==nullptr)returnfalse;...playAnimation(*mAnimation);releaseAnimation(mAnimation);
mAnimation =nullptr;returnfalse;}boolBootAnimation::playAnimation(const Animation& animation){...checkExit();}voidBootAnimation::checkExit(){// Allow surface flinger to gracefully request shutdownchar value[PROPERTY_VALUE_MAX];property_get(EXIT_PROP_NAME, value,"0");int exitnow =atoi(value);if(exitnow){requestExit();}}
总结
init进程是Android系统中的第一个用户空间进程。它负责启动各个系统服务和应用程序。在init进程启动过程中,SurfaceFlinger也被启动,SurfaceFlinger是Android中的显示系统服务,负责管理屏幕显示和图形渲染。开机动画需要使用SurfaceFlinger来显示。然后bootanimation也启动,进行开机动画的播放。bootanimation.zip中包含动画文件和动画配置文件。最终,当所有系统服务和应用程序启动完毕,开机动画结束,进入系统主界面。
版权归原作者 八归少年 所有, 如有侵权,请联系我们删除。