一、Linux开机动画
二、Android开机动画
目前Android开机动画的实现方式主要是:逐帧动画和OpenGL直接绘制。
- 1、逐帧动画
顾名思义,动画都是按帧(Frame)进行播放,每一帧都一张静态的图片,最典型的例子就是电影。它的优点是可以播放很复杂的动画,灵活性强,缺点是数据量比较大。
通过逐帧动画实现开机动画的方法很简单:将一系列图片打包成bootanimation.zip,放到/system/media/目录下,系统将图片一帧一帧播放从而形成动面效果。理论上这种方法可以实现一切动画需求,但是实践后发现当bootanimation.zip大于5M的时候,动画将有明显卡顿,文件越大动面画越不流畅。所以一般手机的开机动画,只有很小的区域展示动画,因为他们的动画帧分辨率很小(如100*50),这样的好处是在较小的文件中,容纳更多的帧。
- 2、OpenGL动画
OpenGL(Open Graphics Library)是个定义了一个跨编程语言、跨平台的应用程序接口(API)的规范,它用于生成二维、三维图像。这个接口由近350个不同的函数调用组成,从简单二维图形绘制到复杂三维图形渲染都能实现。
2.1、开机动画源码分析
涉及代码:
framework/base/cmds/bootanimation/
framework/native/services/surfacefligner/
system/core/init/
启动流程: 内核起来后会启动第一个进程,即init进程。
init进程根据init.rc配置启动surfaceflinger进程。
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()函数。
//frameworks/native/services/surfaceflinger/main_surfaceflinger.cpp
int main(int argc, char** argv) {
....
// instantiate surfaceflinger
sp<SurfaceFlinger> flinger = new SurfaceFlinger(); //创建surfaceflinger服务实例
....
flinger->init();
// publish surface flinger
sp<IServiceManager> sm(defaultServiceManager());
sm->addService(String16(SurfaceFlinger::getServiceName()), flinger, false); //注册到service manager里
// run in this thread
flinger->run(); //开始运行
return 0;
}
首先new一个SurfaceFlinger实例,然后init,然后run
//frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
// 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...");
// Inform native graphics APIs whether the present timestamp is supported:
if (getHwComposer().hasCapability(
HWC2::Capability::PresentFenceIsNotReliable)) {
mStartPropertySetThread = new StartPropertySetThread(false);
} else {
mStartPropertySetThread = new StartPropertySetThread(true);
}
if (mStartPropertySetThread->Start() != NO_ERROR) { //真正启动设置bootanimation的属性线程
ALOGE("Run StartPropertySetThread failed!");
}
ALOGV("Done initializing");
}
初始化graphics之后,mStartPropertySetThread()播放开机动画。//注意已经不是以前的startBootAnim方法
StartPropertySetThread如下定义:
namespace android {
StartPropertySetThread::StartPropertySetThread(bool timestampPropertyValue):
Thread(false), mTimestampPropertyValue(timestampPropertyValue) {}
status_t StartPropertySetThread::Start() {
return run("SurfaceFlinger::StartPropertySetThread", PRIORITY_NORMAL);
}
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;
}
} // namespace android
service bootanim /system/bin/bootanimation
class core animation
user graphics
group graphics audio
disabled
oneshot
writepid /dev/stune/top-app/tasks
bootanim由于设置为disabled,所以它不会自动执行。
这样bootanim进程就会启动?凭什么设置了一个属性就启动了?那么下面我们来看,
/system/core/init/init.cpp,在看init进程的init.cpp的main函数中:
int main(int argc, char** argv) {
......
property_load_boot_defaults();
export_oem_lock_status();
start_property_service(); //start_property_service
set_usb_controller();
}
main函数中调用了start_property_service()方法,该方法声明在/system/core/init/property_service.cpp中:
void start_property_service() {
property_set("ro.property_service.version", "2");
property_set_fd = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
false, 0666, 0, 0, nullptr, sehandle);
if (property_set_fd == -1) {
PLOG(ERROR) << "start_property_service socket creation failed";
exit(1);
}
listen(property_set_fd, 8);
register_epoll_handler(property_set_fd, handle_property_set_fd);
}
在这个函数中创建了一个socket,然后通过epoll机制,去注册了一个监听处理函数。
epoll机制多路复用
init进程会使用epoll机制来轮询事件,其中一个事件是系统属性值被修改。得到该事件后,会执行handle_property_set_fd(),最终调用到handle_property_set():
static void handle_property_set(SocketConnection& socket,
const std::string& name,
const std::string& value,
bool legacy_protocol) {
//......
if (android::base::StartsWith(name, "ctl.")) {
if (check_control_mac_perms(value.c_str(), source_ctx, &cr)) {
handle_control_message(name.c_str() + 4, value.c_str());
if (!legacy_protocol) {
socket.SendUint32(PROP_SUCCESS);
}
} else {
//......
} else {
//......
}
//......
}
该函数会进一步执行handle_control_message(),传入的参数msg.name=ctl.start,msg.value=bootanim
//system/core/init/init.cpp
void handle_control_message(const std::string& msg, const std::string& name) {
Service* svc = ServiceManager::GetInstance().FindServiceByName(name);
if (svc == nullptr) {
LOG(ERROR) << "no such service '" << name << "'";
return;
}
if (msg == "start") {
svc->Start();
} else if (msg == "stop") {
svc->Stop();
} else if (msg == "restart") {
svc->Restart();
} else {
LOG(ERROR) << "unknown control msg '" << msg << "'";
}
}
该函数首先调用FindServiceByName,从service_list中查询要启动的服务是否有存在,若存在,返回服务的相关信息。
因为init.rc中有bootanimation的定义,因此在init进程执行parse_config()时,会将该服务添加到service_list中,所以bootanimation应用是存在的。然后,如果找到了该服务,就调用service_start启动服务。
把service.bootanim.exit属性设为0,这个属性bootanimation进程里会周期检查,=1时就退出动画,这里=0表示要播放动画。
后面通过ctl.start的命令启动bootanimation进程,动画就开始播放了。
下面来到bootanimation的实现
frameworks/base/cmds/bootanimation/bootanimation_main.cpp
int main(int argc, char** argv)
{
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();
waitForSurfaceFlinger();
// create the boot animation object
ALOGV("Boot animation set up. Joining pool.");
sp<BootAnimation> boot;
//创建BootAnimation实例
if(argc > 1){
if(strcmp(argv[1], "shutdown") == 0){
boot = new BootAnimation(new AudioAnimationCallbacks(),true);
mShutdown=true;
}
}else{
boot = new BootAnimation(new AudioAnimationCallbacks(),false);
}
IPCThreadState::self()->joinThreadPool();//binder线程池,与surfaceflinger通信用的。
}
ALOGV("Boot animation exit");
return 0;
}
void BootAnimation::onFirstRef() {
status_t err = mSession->linkToComposerDeath(this);
ALOGE_IF(err, "linkToComposerDeath failed (%s) ", strerror(-err));
if (err == NO_ERROR) {
run("BootAnimation", PRIORITY_DISPLAY);
}
}
status_t BootAnimation::readyToRun() {
mAssets.addDefaultAssets();
sp<IBinder> dtoken(SurfaceComposerClient::getBuiltInDisplay(
ISurfaceComposer::eDisplayIdMain));
DisplayInfo dinfo;
status_t status = SurfaceComposerClient::getDisplayInfo(dtoken, &dinfo);
if (status)
return -1;
// create the native surface
int curWidth = dinfo.w;
int curHeight = dinfo.h;
if(mShutdown && mReverseAxis){
curWidth = dinfo.h;
curHeight = dinfo.w;
}
sp<SurfaceControl> control = session()->createSurface(String8("BootAnimation"),
/*dinfo.w*/curWidth, /*dinfo.h*/curHeight, PIXEL_FORMAT_RGB_565);
SurfaceComposerClient::openGlobalTransaction();
control->setLayer(0x40000000);
SurfaceComposerClient::closeGlobalTransaction();
sp<Surface> s = control->getSurface();
// initialize opengl and egl
const EGLint attribs[] = {
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_DEPTH_SIZE, 0,
EGL_NONE
};
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);
surface = eglCreateWindowSurface(display, config, s.get(), NULL);
context = eglCreateContext(display, config, NULL, NULL);
eglQuerySurface(display, surface, EGL_WIDTH, &w);
eglQuerySurface(display, surface, EGL_HEIGHT, &h);
if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE)
return NO_INIT;
mDisplay = display;
mContext = context;
mSurface = surface;
mWidth = w;
mHeight = h;
mFlingerSurfaceControl = control;
mFlingerSurface = s;
// 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 &&
(access(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE, R_OK) == 0)) {
mZipFileName = SYSTEM_ENCRYPTED_BOOTANIMATION_FILE;
return NO_ERROR;
}
static const char* bootFiles[] = {OEM_BOOTANIMATION_FILE, SYSTEM_BOOTANIMATION_FILE};
static const char* shutdownFiles[] =
{OEM_SHUTDOWNANIMATION_FILE, SYSTEM_SHUTDOWNANIMATION_FILE};
for (const char* f : (/*!mShuttingDown*/!mShutdown ? bootFiles : shutdownFiles)) {
if (access(f, R_OK) == 0) {
mZipFileName = f;
return NO_ERROR;
}
}
//add for boot video
mVideoAnimation = false;
if (access(SYSTEM_BOOTVIDEO_FILE, R_OK) == 0){
mVideoFile = (char*)SYSTEM_BOOTVIDEO_FILE;
} else if (access(DATA_BOOTVIDEO_FILE, R_OK) == 0){
mVideoFile = (char*)DATA_BOOTVIDEO_FILE;
}
property_get("persist.sys.bootvideo.enable",decrypt, "false");
char value[PROPERTY_VALUE_MAX];
property_get("persist.sys.bootvideo.showtime", value, "-1");
if(mVideoFile != NULL && !strcmp(decrypt, "true") &&(atoi(value)!=0)) {
mVideoAnimation = true;
}else{
ALOGD("bootvideo:No boot video animation,EXIT_VIDEO_NAME:%s,bootvideo.showtime:%s\n",decrypt,value);
}
//add end
return NO_ERROR;
}
bool BootAnimation::threadLoop()
{
bool r;
//add for boot video function
mStartbootanimaTime = 0;
mBootVideoTime = -1;
if (mVideoAnimation){
r = video();
} else {
// We have no bootanimation file, so we use the stock android logo
// animation.
if (mZipFileName.isEmpty()) {
r = android();
} else {
r = movie();
}
}
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;
}
Firefly官方提供的 开机log动画修改