android开机动画bootanimation

android开机动画bootanimation

bootanim服务
源码:frameworks/base/cmds/bootanimation/bootanim.rc
service bootanim /system/bin/bootanimation
class core
user graphics
group graphics audio
disabled
oneshot

bootanim服务可执行程序是/system/bin/bootanimation,定义为core类型的核心服务,所有者是graphics,
组是graphics audio默认是disable的,即开机时不会自动启动,oneshot只启动一次,也就是该服务被异常
销毁也不会重启。

之所以设置为disable是因为bootanim服务是依赖于系统的显示服务的,只有当显示相关的服务起来后才能启动
也就是surfaceFlinger服务起来后会拉起bootanim服务显示开机动画。

surfaceFlinger服务
源码:frameworks/native/services/surfaceflinger/surfaceflinger.rc
service surfaceflinger /system/bin/surfaceflinger
class core
user system
group graphics drmrpc readproc
onrestart restart zygote
writepid /sys/fs/cgroup/stune/foreground/tasks

surfaceFlinger是负责显示相关的服务,是非常核心的服务必须一直保持存活的
源码:frameworks/native/cmds/servicemanager/servicemanager.rc
service servicemanager /system/bin/servicemanager
class core
user system
group system readproc
critical
onrestart restart healthd
onrestart restart zygote
onrestart restart audioserver
onrestart restart media
onrestart restart surfaceflinger
onrestart restart inputflinger
onrestart restart drm
onrestart restart cameraserver
writepid /dev/cpuset/system-background/tasks

init进程读取init.rc就会去解析服务,surfaceflinger也是定义的服务之一,调用 /system/bin/surfaceflinger
启动surfaceflinger服务,服务的入口是main函数,下面对surfaceflinger进行分析:

源码:frameworks/native/services/surfaceflinger
入口文件main_surfaceflinger.cpp
int main(int, char**) {
signal(SIGPIPE, SIG_IGN);
// When SF is launched in its own process, limit the number of
// binder threads to 4.
ProcessState::self()->setThreadPoolMaxThreadCount(4); //binder服务池大小限制,最多同时4个线程

// start the thread pool
sp ps(ProcessState::self());
ps->startThreadPool(); //启动线程池监听连接

// instantiate surfaceflinger
sp flinger = new SurfaceFlinger(); //新建一个实例

setpriority(PRIO_PROCESS, 0, PRIORITY_URGENT_DISPLAY); //权限设置

set_sched_policy(0, SP_FOREGROUND); //调度设置

#ifdef ENABLE_CPUSETS
// Put most SurfaceFlinger threads in the system-background cpuset
// Keeps us from unnecessarily using big cores
// Do this after the binder thread pool init
set_cpuset_policy(0, SP_SYSTEM);
#endif

// initialize before clients can connect
flinger->init(); //初始化这里就会启动开机动画

// publish surface flinger
sp sm(defaultServiceManager());
sm->addService(String16(SurfaceFlinger::getServiceName()), flinger, false);

// publish GpuService
sp gpuservice = new GpuService();
sm->addService(String16(GpuService::SERVICE_NAME), gpuservice, false);

// run surface flinger in this thread
flinger->run();

return 0;
}

源码:SurfaceFlinger.cpp
void SurfaceFlinger::init() {
...
// set initial conditions (e.g. unblank default device)
initializeDisplays(); //初始化显示设备

// start boot animation
startBootAnim(); //启动开机动画
}

void SurfaceFlinger::startBootAnim() {
#ifdef MTK_AOSP_ENHANCEMENT
// dynamic disable/enable boot animation
checkEnableBootAnim();
#else
// start boot animation
property_set("service.bootanim.exit", "0"); //为了防止这个属性被设为1,不管现在值怎样都设置为0
property_set("ctl.start", "bootanim"); //启动开机动画
#endif
}

下面分析init property service怎样将属性设置转为对应的动作
在init.cpp中
int main(int argc, char** argv) {
...
start_property_service(); //启动属性管理服务
...
}
start_property_service函数在property_service.cpp中定义:
start_property_service
void start_property_service() {
property_set_fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
0666, 0, 0, NULL);
if (property_set_fd == -1) {
ERROR("start_property_service socket creation failed: %s\n", strerror(errno));
exit(1);
}
listen(property_set_fd, 8); //socket监听binder服务请求,处理相关属性设置动作
register_epoll_handler(property_set_fd, handle_property_set_fd); //epoll监听处理,处理回调函数是handle_property_set_fd
}

handle_property_set_fd
static void handle_property_set_fd()
{
...
switch(msg.cmd) {
...

if (!is_legal_property_name(msg.name, strlen(msg.name))) {
ERROR("sys_prop: illegal property name. Got: \"%s\"\n", msg.name);
close(s);
return;
}
getpeercon(s, &source_ctx);
if (memcmp(msg.name,"ctl.",4) == 0) {
// Keep the old close-socket-early behavior when handling
// ctl.* properties.
close(s);
if (check_control_mac_perms(msg.value, source_ctx, &cr)) {
...
handle_control_message((char*) msg.name + 4, (char*) msg.value);
} else {
...
}
} else {
if (check_mac_perms(msg.name, source_ctx, &cr)) {
...
property_set((char*) msg.name, (char*) msg.value); //普通属性,只是做设置值处理
} else {
}
// Note: bionic's property client code assumes that the
// property server will not close the socket until *AFTER*
// the property is written to memory.
close(s);
}
freecon(source_ctx);
break;
default:
...
}

handle_control_message
void handle_control_message(const std::string& msg, const std::string& name) {
Service* svc = ServiceManager::GetInstance().FindServiceByName(name); //根据只有一个全局Service存储所有定义的服务,后面可以根据名字查找服务
if (svc == nullptr) {
ERROR("no such service '%s'\n", name.c_str());
return;
}
if (msg == "start") {
svc->Start(); //启动服务
} else if (msg == "stop") {
svc->Stop(); //停止服务
} else if (msg == "restart") {
svc->Restart(); //重启服务
} else {
ERROR("unknown control msg '%s'\n", msg.c_str());
}
}

ServiceManager
1.FindServiceByName
Service* ServiceManager::FindServiceByName(const std::string& name) const {
auto svc = std::find_if(services_.begin(), services_.end(),
[&name] (const std::unique_ptr& s) {
return name == s->name();
});
if (svc != services_.end()) {
return svc->get();
}
return nullptr;
}
ServiceManager.h中定义services_向量数组
std::vector> services_;


2.Start
bool Service::Start() {
...
if (!seclabel_.empty()) {
scon = seclabel_;
} else {
...
rc = security_compute_create(mycon, fcon, string_to_security_class("process"),
&ret_scon);
...
if (rc == 0 && scon == mycon) {
ERROR("Service %s does not have a SELinux domain defined.\n", name_.c_str()); //selinux安全检查,在init.rc中定义了服务不能启动会提示这个错误
...
}

pid_t pid = fork();
if (pid == 0) {
std::vector strs;
for (const auto& s : args_) {
strs.push_back(const_cast(s.c_str()));
}
strs.push_back(nullptr);
if ( execve(args_[0].c_str(), (char**) &strs[0], (char**) ENV) < 0) { //启动子进程执行服务程序
ERROR("cannot execve('%s'): %s\n", args_[0].c_str(), strerror(errno));
}
_exit(127);
}

NotifyStateChange("running");

}

NotifyStateChange
void Service::NotifyStateChange(const std::string& new_state) const {
if ((flags_ & SVC_EXEC) != 0) {
// 'exec' commands don't have properties tracking their state.
return;
}
std::string prop_name = StringPrintf("init.svc.%s", name_.c_str()); //init.svc.bootanim
if (prop_name.length() >= PROP_NAME_MAX) {
// If the property name would be too long, we can't set it.
ERROR("Property name \"init.svc.%s\" too long; not setting to %s\n",
name_.c_str(), new_state.c_str());
return;
}

property_set(prop_name.c_str(), new_state.c_str()) ;//设置init.svc.bootanim 为running
}


3. Stop
void Service::Stop() {
StopOrReset(SVC_DISABLED);
}

void Service::StopOrReset(int how) {
/* The service is still SVC_RUNNING until its process exits, but if it has
* already exited it shoudn't attempt a restart yet. */
flags_ &= ~(SVC_RESTARTING | SVC_DISABLED_START);

if ((how != SVC_DISABLED) && (how != SVC_RESET) && (how != SVC_RESTART)) {
/* Hrm, an illegal flag. Default to SVC_DISABLED */
how = SVC_DISABLED;
}
/* if the service has not yet started, prevent
* it from auto-starting with its class
*/
if (how == SVC_RESET) {
flags_ |= (flags_ & SVC_RC_DISABLED) ? SVC_DISABLED : SVC_RESET;
} else {
flags_ |= how;
}

if (pid_) {//如果服务进程已经启动才会有pid_
NOTICE("Service '%s' is being killed...\n", name_.c_str());
kill(-pid_, SIGKILL); //杀死服务进程
NotifyStateChange("stopping"); // 设置init.svc.bootanim 为stopping
} else {
NotifyStateChange("stopped");// 设置init.svc.bootanim 为stopped
}
}

4.Restart
void Service::Restart() {
if (flags_ & SVC_RUNNING) {
/* Stop, wait, then start the service. */
StopOrReset(SVC_RESTART);
} else if (!(flags_ & SVC_RESTARTING)) {
/* Just start the service since it's not running. */
Start();
} /* else: Service is restarting anyways. */
}

经过上面分析,我们已经知道了开机init进程解析init.rc注册bootanim服务到surfaceFlinger服务启动startBootanim到属性服务如何将
属性转化为对应的动作即启动子进程执行 /system/bin/bootanimation整个过程,下面分析bootanimation程序是怎样启动动画的

bootanimation
源码:frameworks/base/cmds/bootanimation/
入口bootanimation_main.cpp
int main(int argc, char** argv)
{
setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_DISPLAY);

char value[PROPERTY_VALUE_MAX];
property_get("debug.sf.nobootanimation", value, "0"); //调试状态可通过设置属性debug.sf.nobootanimation值为1禁止开机动画
int noBootAnimation = atoi(value);
ALOGI_IF(noBootAnimation, "boot animation disabled");
if (!noBootAnimation) {
sp proc(ProcessState::self());
ProcessState::self()->startThreadPool();

bool setBoot = true;
bool setRotated = false;
bool sePaly = true;
if(argc > 1){
if(!strcmp(argv[1],"shut"))
setBoot = false;
}
if(argc > 2){
if(!strcmp(argv[2],"nomp3"))
sePaly = false;
}
if(argc > 3){
if(!strcmp(argv[3],"rotate"))
setRotated = true;
}
ALOGD("[BootAnimation %s %d]setBoot=%d,sePaly=%d,setRotated=%d",__FUNCTION__,__LINE__,setBoot,sePaly,setRotated);
char volume[PROPERTY_VALUE_MAX];
property_get("persist.sys.mute.state", volume, "-1"); //属性persist.sys.mute.state设置音量
int nVolume = -1;
nVolume = atoi(volume);
ALOGD("[BootAnimation %s %d]nVolume=%d",__FUNCTION__,__LINE__,nVolume);
if(nVolume == 0 || nVolume == 1 ){
sePaly = false;
}
ALOGD("before new BootAnimation...");
ALOGD("[BootAnimation %s %d]before new BootAnimation...",__FUNCTION__,__LINE__);
sp boot = new BootAnimation(setBoot,sePaly,setRotated); //智能指针sp,使用后自动释放
ALOGD("joinThreadPool...");
ALOGD("[BootAnimation %s %d]before joinThreadPool...",__FUNCTION__,__LINE__);
IPCThreadState::self()->joinThreadPool(); //等待开机动画线程结束
}
ALOGD("[BootAnimation %s %s %d]end",__FILE__,__FUNCTION__,__LINE__);
return 0;
}

BootAnimation
源码:BootAnimation.cpp
BootAnimation::BootAnimation(bool bSetBootOrShutDown, bool bSetPlayMP3,bool bSetRotated) : Thread(false), mZip(NULL)
{
ALOGD("[BootAnimation %s %d]",__FUNCTION__,__LINE__);
mSession = new SurfaceComposerClient();
bBootOrShutDown = bSetBootOrShutDown;
bShutRotate = bSetRotated;
bPlayMP3 = bSetPlayMP3;
mProgram = 0;
bETC1Movie = false;
mBootVideoPlayType = BOOT_VIDEO_PLAY_FULL;
mBootVideoPlayState = MEDIA_NOP;
ALOGD("[BootAnimation %s %d]bBootOrShutDown=%d,bPlayMP3=%d,bShutRotate=%d",__FUNCTION__,__LINE__,bBootOrShutDown,bPlayMP3,bShutRotate);
}

在BootAnimation.h中,我们可以看到
class BootAnimation : public Thread, public IBinder::DeathRecipient
{
...
private:
virtual bool threadLoop();
virtual status_t readyToRun();
virtual void onFirstRef();
...
}
BootAnimation继承于Thread,而Thread继承RefBase类,并且BootAnimation覆写了onFirstRef,readyToRun,threadLoop函数,
结合智能指针知识,在创建对象BootAnimation时候会首先调用onFirstRef()函数
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); //这里会调用onFirstRef()函数
}
}

status_t BootAnimation::readyToRun() {
if (bBootOrShutDown) {
initBootanimationZip(); //开机动画
} else {
initShutanimationZip(); //关机动画
}
if ((mZip != NULL)&&!(mZipFileName.isEmpty())) {
ZipEntryRO desc = mZip->findEntryByName("desc.txt"); //desc.txt文件描述了如何播放动画,如图片目录,播放次数和播放帧率
uint16_t method;
mZip->getEntryInfo(desc, &method, NULL, NULL, NULL, NULL, NULL);
mZip->releaseEntry(desc);
if (method == ZipFileRO::kCompressStored) {
bETC1Movie = false;
} else {
bETC1Movie = true;
}
}
mAssets.addDefaultAssets();

sp dtoken(SurfaceComposerClient::getBuiltInDisplay(
ISurfaceComposer::eDisplayIdMain));
DisplayInfo dinfo;
status_t status = SurfaceComposerClient::getDisplayInfo(dtoken, &dinfo);
... //下面主要初始化Surface相关的类
sp dtoken(SurfaceComposerClient::getBuiltInDisplay(
ISurfaceComposer::eDisplayIdMain));
DisplayInfo dinfo;
status_t status = SurfaceComposerClient::getDisplayInfo(dtoken, &dinfo);
if (status)
return -1;
/// M: The tablet rotation maybe 90/270 degrees, so set the lcm config for tablet
SurfaceComposerClient::setDisplayProjection(dtoken, DisplayState::eOrientationDefault, Rect(dinfo.w, dinfo.h), Rect(dinfo.w, dinfo.h));

// create the native surface
sp control = session()->createSurface(String8("BootAnimation"), //创建BootAnimation surface
dinfo.w, dinfo.h, PIXEL_FORMAT_RGB_565);

SurfaceComposerClient::openGlobalTransaction();
control->setLayer(0x2000010);
SurfaceComposerClient::closeGlobalTransaction();

sp s = control->getSurface();
...
/*opengl渲染相关初始化*/
EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);

ALOGD("initialize opengl and egl");
EGLBoolean eglret = eglInitialize(display, 0, 0);

//下面是加密相关处理,部分厂家放在initBootanimationZip处理
}


#define GLOBAL_DEVICE_BOOTANIM_OPTR_NAME "persist.operator.optr"
#define REGIONAL_BOOTANIM_FILE_NAME "persist.bootanim.logopath"

void BootAnimation::initBootanimationZip() {

....
f (property_get(GLOBAL_DEVICE_BOOTANIM_OPTR_NAME, OPTR, NULL) > 0){ //获取bootanimation.zip文件的路径
for(int i=0;i
strcpy(BootanimFileName,mResourcePath_gb[i]);
strcat(BootanimFileName,OPTR);
ALOGD("[BootAnimation %s %d]use operator = %s",__FUNCTION__,__LINE__,OPTR);
strcat(BootanimFileName,"/bootanimation.zip");
ALOGD("[BootAnimation %s %d]use the bootanimation zip path = %s",__FUNCTION__,__LINE__,BootanimFileName);
if ((access(BootanimFileName, R_OK) == 0) &&
((zipFile = ZipFileRO::open(BootanimFileName)) != NULL)){
mZip = zipFile;
mZipFileName = BootanimFileName;
break;
}
}
}
...

/*接下来对zip包进行解密*/
property_get("vold.decrypt", decrypt, "");
..
}


threadLoop
根据Thread的周期函数在调用完readyToRun函数后就会调用threadLoop()函数
bool BootAnimation::threadLoop()
{
bool r;
// We have no bootanimation file, so we use the stock android logo
// animation.
sp mediaplayer; //开机音乐设置,播放
const char* resourcePath = initAudioPath();
status_t mediastatus = NO_ERROR;
if (resourcePath != NULL) {
bPlayMP3 = true;
ALOGD("sound file path: %s", resourcePath);
mediaplayer = new MediaPlayer();
mediastatus = mediaplayer->setDataSource(NULL, resourcePath, NULL);

sp listener = new BootVideoListener(this);
mediaplayer->setListener(listener);

if (mediastatus == NO_ERROR) {
ALOGD("mediaplayer is initialized");
Parcel* attributes = new Parcel();
attributes->writeInt32(AUDIO_USAGE_MEDIA); //usage
attributes->writeInt32(AUDIO_CONTENT_TYPE_MUSIC); //audio_content_type_t
attributes->writeInt32(AUDIO_SOURCE_DEFAULT); //audio_source_t
attributes->writeInt32(0); //audio_flags_mask_t
attributes->writeInt32(1); //kAudioAttributesMarshallTagFlattenTags of mediaplayerservice.cpp
attributes->writeString16(String16("BootAnimationAudioTrack")); // tags
mediaplayer->setParameter(KEY_PARAMETER_AUDIO_ATTRIBUTES, *attributes);
mediaplayer->setAudioStreamType(AUDIO_STREAM_MUSIC);
mediastatus = mediaplayer->prepare();
}
if (mediastatus == NO_ERROR) {
ALOGD("media player is prepared");
mediastatus = mediaplayer->start();
}

}else{
bPlayMP3 = false;
}

if ((mZip == NULL)&&(mZipFileName.isEmpty())) { //如果开机动画不存在的情况
r = android(); //加载原生的ANDROID开机图片
} else {
if (!bETC1Movie) {
ALOGD("threadLoop() movie()");
r = movie(); //解析动画,原生开机动画,ANDROID
} else {
ALOGD("threadLoop() ETC1movie()");
r = ETC1movie(); //解析动画,定制的开机动画,bootanimation.zip中desc.txt文件描述动画播放
}
}
if (resourcePath != NULL) {
if (mediastatus == NO_ERROR) {
ALOGD("mediaplayer was stareted successfully, now it is going to be stoped");
mediaplayer->stop();
mediaplayer->disconnect();
mediaplayer.clear();
}
}
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;
}

ETC1movie
先看个desc.txt例子
420 232 20
p 1 0 part0
p 0 10 part1
1355263

第一行表示420*232 20表示1秒播放多少帧(帧率)
第二行p表示动画,也有用c表示的,1表示播放1次,0表示这部分播完到下部分的时间间隔,part0表示播放的图片目录
第三行p(旧版本的)表示动画,也有用c表示(新版本),0表示无限循环播放,10表示两次播放两个part之间间隔的帧数是10,间隔时间是10*1/20秒,只作用于part1

bool BootAnimation::ETC1movie()
{
ZipEntryRO desc = mZip->findEntryByName("desc.txt"); //如果找不到desc.txt则无法播放动画
ALOGE_IF(!desc, "couldn't find desc.txt");
if (!desc) {
return false;
}
...
Animation animation;

// Parse the description file
for (;;) {
const char* endl = strstr(s, "\n");
if (!endl) break;
String8 line(s, endl - s);
const char* l = line.string();
int fps, width, height, count, pause;
char path[ANIM_ENTRY_NAME_MAX];
char color[7] = "000000"; // default to black if unspecified
char pathType;
if (sscanf(l, "%d %d %d", &width, &height, &fps) == 3) { //解析desc.txt第一行
ALOGD("> w=%d, h=%d, fps=%d", width, height, fps); // add log
animation.width = width;
animation.height = height;
animation.fps = fps;
}
else if (sscanf(l, "%c %d %d %s", &pathType, &count, &pause, path) == 4) { //解析desc.txt第一行后面的行
ALOGD("> type=%c, count=%d, pause=%d, path=%s", pathType, count, pause, path); // add log
Animation::Part part;
part.playUntilComplete = pathType == 'c';
part.count = count;
part.pause = pause;
part.path = path;
animation.parts.add(part); //添加值animation中
if (!parseColor(color, part.backgroundColor)) {
ALOGD("> invalid color '#%s'", color);
part.backgroundColor[0] = 0.0f;
part.backgroundColor[1] = 0.0f;
part.backgroundColor[2] = 0.0f;
}
}

s = ++endl;
}

/*读取对应的图片*/
ZipEntryRO entry;
char name[ANIM_ENTRY_NAME_MAX];
while ((entry = mZip->nextEntry(cookie)) != NULL) {
const int foundEntryName = mZip->getEntryFileName(entry, name, ANIM_ENTRY_NAME_MAX);
if (foundEntryName > ANIM_ENTRY_NAME_MAX || foundEntryName == -1) {
ALOGE("Error fetching entry file name");
continue;
}

const String8 entryName(name);
const String8 path(entryName.getPathDir());
const String8 leaf(entryName.getPathLeaf());
if (leaf.size() > 0) {
for (size_t j=0 ; j
if (path == animation.parts[j].path) {
Animation::Frame frame;
frame.name = leaf;
// frame.map = map;
frame.fullPath = entryName;
Animation::Part& part(animation.parts.editItemAt(j));
part.frames.add(frame);
}
}
}
}

mZip->endIteration(cookie);
for (size_t i=0 ; i
const Animation::Part& part(animation.parts[i]);
const size_t fcount = part.frames.size();
ALOGD("[BootAnimation %s %d]i=%zu,i
glBindTexture(GL_TEXTURE_2D, 0);

...
//下面真正开始播放动画
for (int r=0 ; !part.count || r//根据前面解析的part进行循环
// Exit any non playuntil complete parts immediately
if(exitPending() && !part.playUntilComplete) {
//ALOGD("[BootAnimation %s %d]part.playUntilComplete=%d", __FUNCTION__, __LINE__ ,part.playUntilComplete);
break;
}
glClearColor(
part.backgroundColor[0],
part.backgroundColor[1],
part.backgroundColor[2],
1.0f);
for (size_t j=0 ; j//每个part图片播放
const Animation::Frame& frame(part.frames[j]);
nsecs_t lastFrame = systemTime();
//ALOGD("[BootAnimation %s %d]i=%d,r=%d,j=%d,lastFrame=%lld(%lld ms),file=%s",__FUNCTION__,__LINE__,i,r,j,lastFrame,ns2ms(lastFrame),frame.name.string());
if (r > 0) {
glBindTexture(GL_TEXTURE_2D, frame.tid);
} else {
if (part.count != 1) {
glGenTextures(1, &frame.tid);
glBindTexture(GL_TEXTURE_2D, frame.tid);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}
initTexture(frame.fullPath.string());
}

static GLfloat quadVertex[] = { -1.0f, 1.0f, 0.0f, // Position 0
0.0f, 0.0f, // TexCoord 0
-1.0f, -1.0f, 0.0f, // Position 1
0.0f, 1.0f, // TexCoord 1
1.0f, -1.0f, 0.0f, // Position 2
1.0f, 1.0f, // TexCoord 2
1.0f, 1.0f, 0.0f, // Position 3
1.0f, 0.0f // TexCoord 3
};

static GLushort quadIndex[] = { 0, 1, 2, 0, 2, 3 };
3, GL_FLOAT,
false, 5*sizeof(GL_FLOAT), quadVertex);

glVertexAttribPointer(mAttribTexCoord,
2, GL_FLOAT,
false, 5*sizeof(GL_FLOAT), &quadVertex[3]);
glEnableVertexAttribArray(mAttribPosition);
glEnableVertexAttribArray(mAttribTexCoord);

glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, quadIndex);
eglSwapBuffers(mDisplay, mSurface);

nsecs_t now = systemTime();
nsecs_t delay = frameDuration - (now - lastFrame);
//ALOGD("[BootAnimation %s %d]%lld,delay=%lld",__FUNCTION__,__LINE__,ns2ms(now - lastFrame), ns2ms(delay));
lastFrame = now;

if (delay > 0) {
struct timespec spec;
spec.tv_sec = (now + delay) / 1000000000;
spec.tv_nsec = (now + delay) % 1000000000;
int err;
do {
err = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &spec, NULL); //播放的帧率
} while (err<0 && errno == EINTR);
}
if(!bPlayMP3){
checkExit();
}else{
if(mBootVideoPlayState == MEDIA_PLAYBACK_COMPLETE || mBootVideoPlayState == MEDIA_ERROR) {
checkExit(); //检查是否需要退出动画
}
}
}

usleep(part.pause * ns2us(frameDuration)); //两个part之间的时间间隔

// For infinite parts, we've now played them at least once, so perhaps exit
if(exitPending() && !part.count) {
ALOGD("[BootAnimation %s %d]break,exitPending()=%d,part.count=%d",__FUNCTION__,__LINE__,exitPending(),part.count);
break;
}
}
// free the textures for this part
if (part.count != 1) {
for (size_t j=0 ; j
const Animation::Frame& frame(part.frames[j]);
glDeleteTextures(1, &frame.tid);
ALOGD("[BootAnimation %s %d]del,part.count=%d,j=%zu,fcount=%zu",__FUNCTION__,__LINE__,part.count,j,fcount);
}
}
}
ALOGD("[BootAnimation %s %d]end",__FUNCTION__,__LINE__);
return false;
}
至此,开机动画启动过程解析完毕,接下来要进行的是停止开机动画的调用过程。
















你可能感兴趣的:(android)