相关目录:
android/frameworks/base/cmds/bootanimation
android/frameworks/native/services/surfaceflinger
如何启动
开机时android 会根据bootanimation,surfaceflinger的rc文件 ,分别启动相应的服务。
bootanim.rc
service bootanim /system/bin/bootanimation
class core animation
user graphics
group graphics audio
disabled
oneshot
ioprio rt 0
writepid /dev/stune/top-app/tasks
surfaceflinger.rc
service surfaceflinger /system/bin/surfaceflinger
class core animation
priority -20
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
disabled
on init
start surfaceflinger
由于bootanim 启动参数是disabled,导致bootanim 服务并没有执行。bootanim 是通过surfaceflinger来启动的。
surfaceflinger 分析
surfaceflinger的入口位于:android/frameworks/native/services/surfaceflinger/main_surfaceflinger.cpp
int main(int, char**) {
signal(SIGPIPE, SIG_IGN);
...
// instantiate surfaceflinger
sp<SurfaceFlinger> flinger = surfaceflinger::createSurfaceFlinger();
...
// initialize before clients can connect
flinger->init();
...
// run surface flinger in this thread
flinger->run();
...
}
flinger→init 中 启动
sp<StartPropertySetThread> mStartPropertySetThread;
void SurfaceFlinger::init() {
...
if (mStartPropertySetThread->Start() != NO_ERROR) {
ALOGE("Run StartPropertySetThread failed!");
}
...
}
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");
// Start BootAnimation if not started
property_set("ctl.start", "bootanim");
// Exit immediately
return false;
}
在 StartPropertySetThread::threadLoop 中调用了 property_set("ctl.start", "bootanim"); 来启动开机动画。
至于property_set为什么可以实现启动动画服务,我将在其他文章分析。
动画如何加载
启动入口
//android/frameworks/base/cmds/bootanimation/bootanimation_main.cpp
int main()
{
...
//BootAnimation类间接地继承RefBase类,并重写了RefBase类的onFirstRef,
//当一个BootAnimation对象第一次被智能指针引用时,
//BootAnimation对象的成员函数onFirstRef就会被调用。
// create the boot animation object (may take up to 200ms for 2MB zip)
sp<BootAnimation> boot = new BootAnimation(audioplay::createAnimationCallbacks(),0);
if(displayCnt > 1)
{
boot1 = new BootAnimation(audioplay::createAnimationCallbacks(),1);
}
waitForSurfaceFlinger();
boot->run("BootAnimation", PRIORITY_DISPLAY);
...
}
从上面函数可以看到,main函数汇总调用了run,运行这个线程了,从代码中可以看到开机动画的核心就是BootAnimation.
BootAnimation
//开机动画的相关路径
static const char OEM_BOOTANIMATION_FILE[] = "/oem/media/bootanimation.zip";
static const char PRODUCT_BOOTANIMATION_DARK_FILE[] = "/product/media/bootanimation-dark.zip";
static const char PRODUCT_BOOTANIMATION_FILE[] = "/product/media/bootanimation.zip";
static const char SYSTEM_BOOTANIMATION_FILE[] = "/system/media/bootanimation.zip";
static const char APEX_BOOTANIMATION_FILE[] = "/apex/com.android.bootanimation/etc/bootanimation.zip";
static const char PRODUCT_ENCRYPTED_BOOTANIMATION_FILE[] = "/product/media/bootanimation-encrypted.zip";
static const char SYSTEM_ENCRYPTED_BOOTANIMATION_FILE[] = "/system/media/bootanimation-encrypted.zip";
static const char OEM_SHUTDOWNANIMATION_FILE[] = "/oem/media/shutdownanimation.zip";
static const char PRODUCT_SHUTDOWNANIMATION_FILE[] = "/product/media/shutdownanimation.zip";
static const char SYSTEM_SHUTDOWNANIMATION_FILE[] = "/system/media/shutdownanimation.zip";
static const char SYSTEM_DATA_DIR_PATH[] = "/data/system";
static const char SYSTEM_TIME_DIR_NAME[] = "time";
static const char SYSTEM_TIME_DIR_PATH[] = "/data/system/time";
static const char CLOCK_FONT_ASSET[] = "images/clock_font.png";
static const char CLOCK_FONT_ZIP_NAME[] = "clock_font.png";
static const char LAST_TIME_CHANGED_FILE_NAME[] = "last_time_change";
static const char LAST_TIME_CHANGED_FILE_PATH[] = "/data/system/time/last_time_change";
static const char ACCURATE_TIME_FLAG_FILE_NAME[] = "time_is_accurate";
static const char ACCURATE_TIME_FLAG_FILE_PATH[] = "/data/system/time/time_is_accurate";
static const char TIME_FORMAT_12_HOUR_FLAG_FILE_PATH[] = "/data/system/time/time_format_12_hour";
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
bool BootAnimation::preloadAnimation() {
//搜索开机动画文件
findBootAnimationFile();
if (!mZipFileName.isEmpty()) {
//加载路径中的动画
mAnimation = loadAnimation(mZipFileName);
return (mAnimation != nullptr);
}
return false;
}
BootAnimation::findBootAnimationFile() {
if (!mShuttingDown && encryptedAnimation) {
...
}
//加载默认动画路径
static const char* bootFiles[] = \
{APEX_BOOTANIMATION_FILE, playDarkAnim ? PRODUCT_BOOTANIMATION_DARK_FILE : PRODUCT_BOOTANIMATION_FILE,OEM_BOOTANIMATION_FILE, SYSTEM_BOOTANIMATION_FILE};
static const char* shutdownFiles[] =\
{PRODUCT_SHUTDOWNANIMATION_FILE, OEM_SHUTDOWNANIMATION_FILE, SYSTEM_SHUTDOWNANIMATION_FILE, ""};
}
BootAnimation::Animation* BootAnimation::loadAnimation(const String8& fn)
{
//打开zip压缩包
ZipFileRO *zip = ZipFileRO::open(fn);
Animation *animation = new Animation;
animation->fileName = fn;
animation->zip = zip;
animation->clockFont.map = nullptr;
mLoadedFiles.add(animation->fileName);
//解析压缩包中的desc
parseAnimationDesc(*animation);
mLoadedFiles.remove(fn);
return animation;
}
bool BootAnimation::parseAnimationDesc(Animation& animation)
{
//读取压缩包 desc.txt
readFile(animation.zip, "desc.txt", desString)
// Parse the description file
for (;;) {
...
}
}
以上是读取系统中的开机动画相关文件,那么系统是如何播放动画的那?
BootAnimation::threadLoop
初始化完成之后,开始执行线程函数
bool BootAnimation::threadLoop()
{
//判断开机动画类型 图片或者动画
if (mZipFileName.isEmpty()) {
//播放图片
//You can change logo for this displayId;
//images/android-logo-mask.png can be changed to other picture
//android\frameworks\base\core\res\assets\images\android-logo-mask.png
r = android();
} else {
//如果不为空,读取目录中的开机动画文件
r = movie();
}
}
//动画播放完成 clear opengl
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 r;
从上面代码可以知道android() 和movie() 是最后播放开机动画的核心函数,如果没有动画就执行android()播放默认的图片,如果有动画则调用movie()播放动画。
bool BootAnimation::movie()
{
//加载动画
mAnimation = loadAnimation(mZipFileName);
//调用audioplay->init,播放音频
for (const Animation::Part& part : mAnimation->parts) {
if (part.animation != nullptr) {
mCallbacks->init(part.animation->parts);
}
}
//配置Opengl
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glShadeModel(GL_FLAT);
glDisable(GL_DITHER);
...
//根据mAnimation size 播放序列帧
playAnimation(*mAnimation);
//释放动画
releaseAnimation(mAnimation);
}
总结:
从整体来看开机动画模块是安卓系统中最简单的一个模块,文件少,代码简单。对于初学者来说,bootanimation 是入门android底层开发最好的模块之一了,从模块中我们能够大概知道Android代码的设计思路和代码编写规则。