android 车机 BootAnimation开机动画代码分析

相关目录:

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代码的设计思路和代码编写规则。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值