前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android开机动画启动流程

Android开机动画启动流程

作者头像
八归少年
发布2023-11-14 10:32:22
5910
发布2023-11-14 10:32:22
举报
文章被收录于专栏:program

首语

在Android设备开机启动时,会展示Android开机动画,用于增加用户体验和展示设备品牌等信息。它也是Android系统启动的一部分。开机动画是由bootanimation负责的,因此首先先了解下bootanimation是如何启动的。

bootanimation 启动脚本分析

init进程中第二阶段(SecondStageMain)的主要工作有初始化属性服务,加载启动脚本,解析init.rc文件等。

源码路径:system/core/init/init.cpp

代码语言:javascript
复制
int SecondStageMain(int argc, char** argv) {
    ...
    LoadBootScripts(am, sm);
    ...
}
static void LoadBootScripts(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

代码语言:javascript
复制
...
    # Start standard binderized HAL daemons
    class_start hal

    class_start core
...

源码路径:frameworks/base/cmds/bootanimation/bootanim.rc

代码语言:javascript
复制
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

代码语言:javascript
复制
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

代码语言:javascript
复制
int main(int, char**) {
    ...
    // instantiate surfaceflinger
    sp<SurfaceFlinger> flinger = surfaceflinger::createSurfaceFlinger();
    ...
    // initialize before clients can connect
    flinger->init();
    ...
}

源码路径:frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp

代码语言:javascript
复制
void SurfaceFlinger::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

代码语言:javascript
复制
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");
    property_set("service.bootanim.progress", "0");
    // Start BootAnimation if not started
    property_set("ctl.start", "bootanim");
    // Exit immediately
    return false;
}

bootanimation启动流程

分析bootanimation的main函数,首先判断是否禁用了启动动画,没有则创建一个binder线程池,再创建BootAnimation,等待SurfaceFlinger启动完成。

源码路径:frameworks/base/cmds/bootanimation/bootanimation_main.cpp

代码语言:javascript
复制
int main()
{
    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 = new BootAnimation(audioplay::createAnimationCallbacks());

        waitForSurfaceFlinger();

        boot->run("BootAnimation", PRIORITY_DISPLAY);

        ALOGV("Boot animation set up. Joining pool.");

        IPCThreadState::self()->joinThreadPool();
    }
    return 0;
}

源码路径:frameworks/base/cmds/bootanimation/BootAnimationUtil.cpp

代码语言:javascript
复制
bool bootAnimationDisabled() {
    char value[PROPERTY_VALUE_MAX];
    //启动动画调试模式
    property_get("debug.sf.nobootanimation", value, "0");
    if (atoi(value) > 0) {
        return true;
    }

    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)) {
            return true;
        }
    }

    return false;
}

void waitForSurfaceFlinger() {
    // TODO: replace this with better waiting logic in future, b/35253872
    int64_t waitStartTime = elapsedRealtime();
    sp<IServiceManager> sm = defaultServiceManager();
    const String16 name("SurfaceFlinger");
    const int SERVICE_WAIT_SLEEP_MS = 100;
    const int 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

代码语言:javascript
复制
private:
    virtual bool        threadLoop();
    virtual status_t    readyToRun();
    virtual void        onFirstRef();
    virtual void        binderDied(const wp<IBinder>& who);
	...
	//系统默认的开机画面
	bool android();
	//用户自定义的开机动画
    bool movie();
	...
    //加载动画
	Animation* loadAnimation(const String8&);
	//播放动画
    bool playAnimation(const Animation&);
    void releaseAnimation(Animation*) const;
    bool parseAnimationDesc(Animation&);
    bool preloadZip(Animation &animation);
    void findBootAnimationFile();
    bool findBootAnimationFileInternal(const std::vector<std::string>& files);
    bool preloadAnimation();
    EGLConfig getEglConfig(const EGLDisplay&);
    ui::Size limitSurfaceSize(int width, int height) const;
    void resizeSurface(int newWidth, int newHeight);
    void projectSceneToWindow();

    bool shouldStopPlayingPart(const Animation::Part& part, int fadedFramesCount,
                               int lastDisplayedProgress);
	//检查是否退出动画
    void checkExit();

BootAnimation构造函数中,创建了SurfaceComposerClient,mSession用来和SurfaceFlinger执行Binder进程间通信,执行linkToComposerDeath方法用于获取SurfaceFlinger死亡通知,preloadAnimation方法开始加载动画,首先去查询动画文件,动画文件的存放位置如代码中定义所示。动画文件是按照指定位置顺序读取,如果读取到当前位置动画文件,则不读取后续动画文件。

源码路径:frameworks/base/cmds/bootanimation/BootAnimation.cpp

代码语言:javascript
复制
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 constexpr const char* PRODUCT_USERSPACE_REBOOT_ANIMATION_FILE = "/product/media/userspace-reboot.zip";
static constexpr const char* OEM_USERSPACE_REBOOT_ANIMATION_FILE = "/oem/media/userspace-reboot.zip";
static constexpr const char* SYSTEM_USERSPACE_REBOOT_ANIMATION_FILE = "/system/media/userspace-reboot.zip";

BootAnimation::BootAnimation(sp<Callbacks> callbacks)
        : Thread(false), mLooper(new Looper(false)), mClockEnabled(true), mTimeIsAccurate(false),
        mTimeFormat12Hour(false), mTimeCheckThread(nullptr), mCallbacks(callbacks) {
    mSession = new SurfaceComposerClient();

    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());
}
void BootAnimation::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 wait
        ALOGD("%sAnimationPreloadTiming start time: %" PRId64 "ms",
                mShuttingDown ? "Shutdown" : "Boot", elapsedRealtime());
        preloadAnimation();
        ALOGD("%sAnimationPreloadStopTiming start time: %" PRId64 "ms",
                mShuttingDown ? "Shutdown" : "Boot", elapsedRealtime());
    }
}
bool BootAnimation::preloadAnimation() {
    findBootAnimationFile();
    if (!mZipFileName.isEmpty()) {
        //加载动画
        mAnimation = loadAnimation(mZipFileName);
        return (mAnimation != nullptr);
    }

    return false;
}
void BootAnimation::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) {
        static const std::vector<std::string> encryptedBootFiles = {
            PRODUCT_ENCRYPTED_BOOTANIMATION_FILE, SYSTEM_ENCRYPTED_BOOTANIMATION_FILE,
        };
        if (findBootAnimationFileInternal(encryptedBootFiles)) {
            return;
        }
    }

    const bool playDarkAnim = android::base::GetIntProperty("ro.boot.theme", 0) == 1;
    static const std::vector<std::string> bootFiles = {
        APEX_BOOTANIMATION_FILE, playDarkAnim ? PRODUCT_BOOTANIMATION_DARK_FILE : PRODUCT_BOOTANIMATION_FILE,
        OEM_BOOTANIMATION_FILE, SYSTEM_BOOTANIMATION_FILE
    };
    static const std::vector<std::string> shutdownFiles = {
        PRODUCT_SHUTDOWNANIMATION_FILE, OEM_SHUTDOWNANIMATION_FILE, SYSTEM_SHUTDOWNANIMATION_FILE, ""
    };
    static const 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);
    } else if (mShuttingDown) {
        findBootAnimationFileInternal(shutdownFiles);
    } else {
        findBootAnimationFileInternal(bootFiles);
    }
}
bool BootAnimation::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();
            return true;
        }
    }
    return false;
}
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());
        return nullptr;
    }
    ZipFileRO *zip = ZipFileRO::open(fn);
    if (zip == nullptr) {
        SLOGE("Failed to open animation zip \"%s\": %s",
            fn.string(), strerror(errno));
        return nullptr;
    }

    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);
        return nullptr;
    }

    mLoadedFiles.remove(fn);
    return animation;
}
bool BootAnimation::parseAnimationDesc(Animation& animation)  {
    String8 desString;

    if (!readFile(animation.zip, "desc.txt", desString)) {
        return false;
    }
    char const* 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 次。

代码语言:javascript
复制
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 目录中加载帧并进行播放。

代码语言:javascript
复制
p 1 0 folder1 

加载动画执行完成后,接下来会执行主体函数threadLoop,首先判断自定义开机动画文件是否存在,如果不存在则执行Android方法,否则执行自定义动画Movie方法。Android和Movie方法最后都返回false。因此threadloop也返回false,代表代码只执行一次。最后获取service.bootanim.exit属性,如果值为1,循环就会退出,开机动画就会结束。service.bootanim.exit属性是在AMS中被修改为1的,在后面AMS中会讲到。

代码语言:javascript
复制
static const char EXIT_PROP_NAME[] = "service.bootanim.exit";
bool BootAnimation::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;
}

bool BootAnimation::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({});
   	...
    return false;
}
bool BootAnimation::movie() {
    if (mAnimation == nullptr) {
        mAnimation = loadAnimation(mZipFileName);
    }

    if (mAnimation == nullptr)
        return false;
	...
    playAnimation(*mAnimation);
    releaseAnimation(mAnimation);
    mAnimation = nullptr;

    return false;
}
bool BootAnimation::playAnimation(const Animation& animation) {
    ...
    checkExit();
}
void BootAnimation::checkExit() {
    // Allow surface flinger to gracefully request shutdown
    char 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中包含动画文件和动画配置文件。最终,当所有系统服务和应用程序启动完毕,开机动画结束,进入系统主界面。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 首语
  • bootanimation 启动脚本分析
  • SurfaceFlinger启动流程
  • bootanimation启动流程
    • bootanimation.zip
    • 总结
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档