[Android 13]开机动画原理分析

这篇具有很好参考价值的文章主要介绍了[Android 13]开机动画原理分析。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Android开机动画

hongxi.zhu 2023-6-12
Lineageos_20(Android T) on Pixel 2XL

一、 开机动画的启动

1.1 init.rc启动相应的进程

开机动画跑起来除了需要自身进程的启动外,还肯定以来显示系统的相关进程,即一定需要SurfaceFlinger的进程的合成和送显,所以这里需要启动SurfaceFlinger服务和bootanim服务,两者是在init.rc中启动。rc的触发阶段和引用关系如下:

  • system/core/rootdir/init.rc
...
import /init.${ro.hardware}.rc
import /vendor/etc/init/hw/init.${ro.hardware}.rc
...

# Mount filesystems and start core system services.  //在这个阶段就可以启动系统核心的服务了,包括SF等
on late-init
	...
    # Mount fstab in init.{$device}.rc by mount_all with '--late' parameter
    # to only mount entries with 'latemount'. This is needed if '--early' is
    # specified in the previous mount_all command on the fs stage.
    # With /system mounted and properties form /system + /factory available,
    # some services can be started.
    trigger late-fs
	...
  • device/google/wahoo/init.hardware.rc
on late-fs
    # Start devices by sysfs trigger
    start vendor.devstart_sh
    # Start services for bootanim
    start vendor.power-hal-1-3
    start surfaceflinger  # 先启动surfaceflinger
    start bootanim        # 然后是bootanim
    start vendor.hwcomposer-2-1
    start vendor.configstore-hal
    start vendor.gralloc-2-0
	...

这里说下有时候这个文件源码里名字和设备里的名字不一样,是因为这个文件编译时会改名并拷贝到vendor/etc/init/hw/init.taimen.rc,可以从device.mk看出,我的设备Pixel 2XL taimen派生于wahoo,会引用wahoo的device.mk

  • device/google/wahoo/device.mk
  PRODUCT_COPY_FILES += \
    $(LOCAL_PATH)/init.hardware.rc:$(TARGET_COPY_OUT_VENDOR)/etc/init/hw/init.$(PRODUCT_HARDWARE).rc

上面init.hardware.rc中start的surfaceflingerbootanim是在它们各自的module下声明的rc文件,当build的时候,编译系统会解析Android.bp或者Android.mk获取对应的目标的rc文件,并将rc的内容include到主rc中。

  • SurfaceFlinger服务的启动文件
    frameworks/native/services/surfaceflinger/surfaceflinger.rc
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
  • bootanim服务的启动文件
    frameworks/base/cmds/bootanimation/bootanim.rc
service bootanim /system/bin/bootanimation
    class core animation
    user graphics
    group graphics audio
    disabled
    oneshot
    ioprio rt 0
    task_profiles MaxPerformance

1.2 surfaceflinger服务的启动

动画播放依赖于SF进程,所以它希望先启动(无法保证两者一定能有序的启动),SF进程启动后会加载它的main方法
frameworks/native/services/surfaceflinger/main_surfaceflinger.cpp

int main(int, char**) {
	...
    // instantiate surfaceflinger
    sp<SurfaceFlinger> flinger = surfaceflinger::createSurfaceFlinger();
	...
    // initialize before clients can connect
    flinger->init(); // 这里init会去设置开机动画相关的属性

    // publish surface flinger
    sp<IServiceManager> sm(defaultServiceManager());
    sm->addService(String16(SurfaceFlinger::getServiceName()), flinger, false,
                   IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL | IServiceManager::DUMP_FLAG_PROTO);

    // publish gui::ISurfaceComposer, the new AIDL interface
    sp<SurfaceComposerAIDL> composerAIDL = new SurfaceComposerAIDL(flinger);
    sm->addService(String16("SurfaceFlingerAIDL"), composerAIDL, false,
                   IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL | IServiceManager::DUMP_FLAG_PROTO);

    startDisplayService(); // dependency on SF getting registered above
	...
    // run surface flinger in this thread
    flinger->run();  //SurfaceFinger启动,并运行在主线程,通过消息机制循环等待任务

    return 0;
}

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...");
	...
    mStartPropertySetThread = getFactory().createStartPropertySetThread(presentFenceReliable);
	// 开启一个子现场去设置开机动画相关的属性(主线程设置属性会造成block)
    if (mStartPropertySetThread->Start() != NO_ERROR) {
        ALOGE("Run StartPropertySetThread failed!");
    }

    ALOGV("Done initializing");
}

frameworks/native/services/surfaceflinger/StartPropertySetThread.cpp

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");  //启动开机动画的属性,这里保险起见还是会去启动bootanim服务,但是bootanim服务有可能在前面已经被init进程拉起来了
    // Exit immediately
    return false;  //只执行一次,设置完就退出
}

这里主要是做了两个事:

  1. 重置开机动画的退出属性,开机动画进程会循环check这个属性,如果为1就结束播放并退出,这里先初始化为0
  2. 设置开机动画启动属性,如果开机动画进程没有被前面的init进程拉起来,那么这里还会再次主动让属性服务拉起开机动画进程。

接下来就是进入启动开机动画进程的流程了。

1.3 开机动画进程的启动

frameworks/base/cmds/bootanimation/bootanimation_main.cpp

int main()
{
    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();

        // create the boot animation object (may take up to 200ms for 2MB zip)
        sp<BootAnimation> boot = new BootAnimation(audioplay::createAnimationCallbacks()); //创建动画对象,这个过程会去解析的动画文件,文件越大越耗时

        waitForSurfaceFlinger();  //向servicemanager检查SF进程是否正常启动了, 死循环等待SF注册成功

        boot->run("BootAnimation", PRIORITY_DISPLAY); //开始跑动画逻辑

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

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

主要是:

  • 检查是否禁用了开机动画,如果禁用了进程直接结束,不播放Android动画(也许厂商自己用其他方式实现)
  • 创建BootAnimation对象,加载并解析动画文件,加载时间根据文件大小有关(图片大小,数量)
  • 循环等待SurfaceFlinger服务的启动(没有SF也播放不了动画)
  • 启动动画线程(BootAnimation继承于Thread类,它本身就是一个thread),开始播放动画
1.3.1 检查是否禁用了开机动画
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;
}

主要受三个属性控制,三个属性优先级有前后。

1.3.2 创建BootAnimation对象,预加载动画文件
BootAnimation::BootAnimation(sp<Callbacks> callbacks)
        : Thread(false), mLooper(new Looper(false)), mClockEnabled(true), mTimeIsAccurate(false),
        mTimeFormat12Hour(false), mTimeCheckThread(nullptr), mCallbacks(callbacks) {
    mSession = new SurfaceComposerClient(); // 获取SF在开机动画进程的代理,后面会使用该对象与SF跨进程通信

    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());
}

上面的构造方法中仅仅中只是获取了SF的session代理对象, 真正的加载逻辑在这个对象的第一次引用回调方法中(在前面的sp指针实例化时被回调)。

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(),这个过程主要是解析bootanimation.zip文件

bool BootAnimation::preloadAnimation() {
    findBootAnimationFile();  // 检索系统的几个预设定路径下是否存在bootanimation.zip文件
    if (!mZipFileName.isEmpty()) {
        mAnimation = loadAnimation(mZipFileName);  // 加载动画文件
        return (mAnimation != nullptr);
    }

    return false;
}

findBootAnimationFile方法是去检索系统的几个预设定路径下是否存在bootanimation.zip文件, 如果存在赋值给mZipFileName, 几个预设定路径是:(寻找是根据系统是否加密、是否是深色主题进行选择,pixel 2xl的文件是 /product/media/下)

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.bootani开始播放动画mation/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";

loadAnimation()方法是去解析bootanimation.zip文件

BootAnimation::Animation* BootAnimation::loadAnimation(const String8& fn) {
    if (mLoadedFiles.indexOf(fn) >= 0) {  //mLoadedFiles是根据文件名是否加载过来防止加载动画zip包多次
        SLOGE("File \"%s\" is already loaded. Cyclic ref is not allowed",
            fn.string());
        return nullptr;
    }
    ZipFileRO *zip = ZipFileRO::open(fn);  //打开zip文件
    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);  //整个zip也用一个Animation来描述

    parseAnimationDesc(*animation);  //解析‘desc.txt’文件,根据这个文件创建每个part的Animation对象
    if (!preloadZip(*animation)) {  //加载每一个part对应的图片,并填充到animation.part.frames
        releaseAnimation(animation);
        return nullptr;
    }

    mLoadedFiles.remove(fn);
    return animation;
}

解析desc.txt文件

bool BootAnimation::parseAnimationDesc(Animation& animation)  {
	...
    // Parse the description file
    for (;;) {
        const char* endl = strstr(s, "\n");
        if (endl == nullptr) break;
        String8 line(s, endl - s);
        const char* l = line.string();
        int fps = 0;
        int width = 0;
        int height = 0;
        int count = 0;
        int pause = 0;
        int progress = 0;
        int framesToFadeCount = 0;
        int colorTransitionStart = 0;
        int colorTransitionEnd = 0;
        char path[ANIM_ENTRY_NAME_MAX];
        char color[7] = "000000"; // default to black if unspecified
        char clockPos1[TEXT_POS_LEN_MAX + 1] = "";
        char clockPos2[TEXT_POS_LEN_MAX + 1] = "";
        char dynamicColoringPartNameBuffer[ANIM_ENTRY_NAME_MAX];
        char pathType;
        // start colors default to black if unspecified
        char start_color_0[7] = "000000";
        char start_color_1[7] = "000000";
        char start_color_2[7] = "000000";
        char start_color_3[7] = "000000";

        int nextReadPos;

        int topLineNumbers = sscanf(l, "%d %d %d %d", &width, &height, &fps, &progress);
        if (topLineNumbers == 3 || topLineNumbers == 4) { //解析第一行,获取width/height/fps参数
            // SLOGD("> w=%d, h=%d, fps=%d, progress=%d", width, height, fps, progress);
            animation.width = width;
            animation.height = height;
            animation.fps = fps;
            if (topLineNumbers == 4) {  //如果配置了progress,一般不会配置
              animation.progressEnabled = (progress != 0);
            } else {
              animation.progressEnabled = false;
            }
        } else if (sscanf(l, "dynamic_colors %" STRTO(ANIM_PATH_MAX) "s #%6s #%6s #%6s #%6s %d %d",
            dynamicColoringPartNameBuffer,加载每一个part对应的图片,并填充到animation.part.frames
            start_color_0, start_color_1, start_color_2, start_color_3,
            &colorTransitionStart, &colorTransitionEnd)) {  //动态颜色,我们一般不配置,所以不会走这里
            animation.dynamicColoringEnabled = true;
            parseColor(start_color_0, animation.startColors[0]);
            parseColor(start_color_1, animation.startColors[1]);
            parseColor(start_color_2, animation.startColors[2]);
            parseColor(start_color_3, animation.startColors[3]);
            animation.colorTransitionStart = colorTransitionStart;
            animation.colorTransitionEnd = colorTransitionEnd;
            dynamicColoringPartName = std::string(dynamicColoringPartNameBuffer);
        } else if (sscanf(l, "%c %d %d %" STRTO(ANIM_PATH_MAX) "s%n",
                          &pathType, &count, &pause, path, &nextReadPos) >= 4) {  //解析第二行开始的part部分
            if (pathType == 'f') {
                sscanf(l + nextReadPos, " %d #%6s %16s %16s", &framesToFadeCount, color, clockPos1,
                       clockPos2);
            } else {
                sscanf(l + nextReadPos, " #%6s %16s %16s", color, clockPos1, clockPos2);
            }
            // SLOGD("> type=%c, count=%d, pause=%d, path=%s, framesToFadeCount=%d, color=%s, "
            //       "clockPos1=%s, clockPos2=%s",
            //       pathType, count, pause, path, framesToFadeCount, color, clockPos1, clockPos2);
            Animation::Part part; 
            if (path == dynamicColoringPartName) {
                // Part is specified to use dynamic coloring.
                part.useDynamicColoring = true;
                part.postDynamicColoring = false;
                postDynamicColoring = true;
            } else {   //我们一般只配置pathType、count、pause,其他的都是空
                // Part does not use dynamic coloring.
                part.useDynamicColoring = false;
                part.postDynamicColoring =  postDynamicColoring;
            }
            part.playUntilComplete = pathType == 'c';
            part.framesToFadeCount = framesToFadeCount;
            part.count = count;
            part.pause = pause;加载
            part.path = path;
            part.audioData = nullptr;  //新版本还加入了开机动画每个part可以添加对应的音频文件,但是一般不配置,且不是在这里加载
            part.animation = nullptr;  //这里每个part下面一般不会再嵌套动画文件了,所以它已经是节点了
            if (!parseColor(color, part.backgroundColor)) {  //color和backgroundcolor没有特殊配置使用默认的black
                SLOGE("> invalid color '#%s'", color);
                part.backgroundColor[0] = 0.0f;
                part.backgroundColor[1] = 0.0f;
                part.backgroundColor[2] = 0.0f;
            }
            parsePosition(clockPos1, clockPos2, &part.clockPosX, &part.clockPosY);
            animation.parts.add(part);  //将part加入animation对象中,这样就能拿到他的fps/type/count/pause, 但是frames数据还没有填充
        }
        else if (strcmp(l, "$SYSTEM") == 0) {  //一种特殊情况,不会走这里
            // SLOGD("> SYSTEM");
            Animation::Part part;
            part.playUntilComplete = false;
            part.framesToFadeCount = 0;
            part.count = 1;
            part.pause = 0;
            part.audioData = nullptr;
            part.animation = loadAnimation(String8(SYSTEM_BOOTANIMATION_FILE));
            if (part.animation != nullptr)
                animation.parts.add(part);
        }
        s = ++endl;
    }

    return true;
}

解析完desc.txt,已经将配置文件中每个part对应到每个实际的part文件夹,但是真正的图片,还没有去加载,接下来preloadZip加载每一个part对应的图片,并填充到animation.part.frames

bool BootAnimation::preloadZip(Animation& animation) {
    // read all the data structures
    const size_t pcount = animation.parts.size();
    void *cookie = nullptr;
    ZipFileRO* zip = animation.zip;
    if (!zip->startIteration(&cookie)) {
        return false;
    }

    ZipEntryRO entry;
    char name[ANIM_ENTRY_NAME_MAX];
    while ((entry = zip->nextEntry(cookie)) != nullptr) {  //遍历整个zip下的文件树,一般我们都不会嵌套多层,叶子节点就是每个part文件夹下的文件
        const int foundEntryName = zip->getEntryFileName(entry, name, ANIM_ENTRY_NAME_MAX);
        if (foundEntryName > ANIM_ENTRY_NAME_MAX || foundEntryName == -1) {
            SLOGE("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 < pcount; j++) {  //遍历每个part
                if (path == animation.parts[j].path) {
                    uint16_t method;
                    // supports only stored png files
                    if (zip->getEntryInfo(entry, &method, nullptr, nullptr, nullptr, nullptr, nullptr)) {
                        if (method == ZipFileRO::kCompressStored) {  //从直接可以知道bootanimation.zip必须使用存储方式打包,不能压缩,否则将不能播放
                            FileMap* map = zip->createEntryFileMap(entry);
                            if (map) {
                                Animation::Part& part(animation.parts.editItemAt(j));
                                if (leaf == "audio.wav") { // 如果是音频文件
                                    // a part may have at most one audio file
                                    part.audioData = (uint8_t *)map->getDataPtr();
                                    part.audioLength = map->getDataLength();
                                } else if (leaf == "trim.txt") {  //一般不会使用trim
                                    part.trimData.setTo((char const*)map->getDataPtr(),
                                                        map->getDataLength());
                                } else {
                                    Animation::Frame frame;
                                    frame.name = leaf;
                                    frame.map = map;
                                    frame.trimWidth = animation.width;
                                    frame.trimHeight = animation.height;
                                    frame.trimX = 0;
                                    frame.trimY = 0;
                                    part.frames.add(frame); // 将part下面的图片添加到part的frames
                                }
                            }
                        } else {
                            SLOGE("bootanimation.zip is compressed; must be only stored");
                        }
                    }
                }
            }
        }
    }
	...
    zip->endIteration(cookie);

    return true;
}

到这里动画文件的预加载就完成了,这时候需要check SF是否已经正常启动。

1.3.3 等待SF服务的启动

frameworks/base/cmds/bootanimation/BootAnimationUtil.cpp

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

向ServiceMananger询问SF是否注册,如果没注册死循环,每秒问一次,启动了就开始播放动画

1.3.4 启动播放线程,播放动画

前面的main方法的boot->run("BootAnimation", PRIORITY_DISPLAY);会先走BootAnimation线程的readyToRun, 然后才执行真正的线程循环体threadLoop,先看下readyToRun

  • readyToRun
status_t BootAnimation::readyToRun() {
	...
    mDisplayToken = SurfaceComposerClient::getInternalDisplayToken();  //获取DisplayToken, 用于获取Display(屏幕)的参数
    if (mDisplayToken == nullptr)
        return NAME_NOT_FOUND;

    DisplayMode displayMode;
    const status_t error =
            SurfaceComposerClient::getActiveDisplayMode(mDisplayToken, &displayMode);  //获取DisplayMode,用于获取分辨率等信息
	...
    resolution = limitSurfaceSize(resolution.width, resolution.height);
    // create the native surface
    sp<SurfaceControl> control = session()->createSurface(String8("BootAnimation"),
            resolution.getWidth(), resolution.getHeight(), PIXEL_FORMAT_RGB_565);  //创建Surface并返回一个surfacecontrol对象,动画需要他通过opengl绘制到surface上

    SurfaceComposerClient::Transaction t;  //创建与SF通信的事务
	...
	
    // Scale forced resolution to physical resolution
    Rect forcedRes(0, 0, resolution.width, resolution.height);
    Rect physRes(0, 0, displayMode.resolution.width, displayMode.resolution.height);
    t.setDisplayProjection(mDisplayToken, ui::ROTATION_0, forcedRes, physRes);  //设置屏幕的投影参数

    t.setLayer(control, 0x40000000) //将Layer和SurfaceControl绑定
        .apply(); //提交事务

    sp<Surface> s = control->getSurface(); //获取一个surface

	//初始化opengl and egl
    // initialize opengl and egl
    EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
    eglInitialize(display, nullptr, nullptr);
    EGLConfig config = getEglConfig(display);
    EGLSurface surface = eglCreateWindowSurface(display, config, s.get(), nullptr);
    // Initialize egl context with client version number 2.0.
    EGLint contextAttributes[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
    EGLContext context = eglCreateContext(display, config, nullptr, contextAttributes);
    EGLint w, h;
    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;
    mInitWidth = mWidth = w;
    mInitHeight = mHeight = h;
    mFlingerSurfaceControl = control;
    mFlingerSurface = s;
    mTargetInset = -1;
    
	...
	
    projectSceneToWindow(); // 裁剪窗口、转化坐标

    // Register a display event receiver
    mDisplayEventReceiver = std::make_unique<DisplayEventReceiver>();
    status_t status = mDisplayEventReceiver->initCheck();
    SLOGE_IF(status != NO_ERROR, "Initialization of DisplayEventReceiver failed with status: %d",
            status);
    mLooper->addFd(mDisplayEventReceiver->getFd(), 0, Looper::EVENT_INPUT,
            new DisplayEventCallback(this), nullptr);

    return NO_ERROR;
}

  • threadLoop
bool BootAnimation::threadLoop() {
    bool result;
    initShaders();  //初始化opengl着色器

    // We have no bootanimation file, so we use the stock android logo
    // animation.
    if (mZipFileName.isEmpty()) {
        ALOGD("No animation file");
        result = android();  //当我们没有定制bootanimation.zip(不存在这个文件)时,系统会去播放assets下面那两张图片,一个android字样的闪烁动画
    } else {
        result = movie();  //当存在bootanimation.zip时,走这个分支
    }

    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;
}

我们主要看下movie的情况,这个名字也是很直白,开机动画的实现是图片逐帧动画,和电影的那种原理相同

bool BootAnimation::movie() {
	...
    // mCallbacks->init() may get called recursively,
    // this loop is needed to get the same results
    for (const Animation::Part& part : mAnimation->parts) {
        if (part.animation != nullptr) {
            mCallbacks->init(part.animation->parts); // 初始化每一个part的音频(一般没音频)
        }
    }
    mCallbacks->init(mAnimation->parts);
	...
	//配置Blend
    // Blend required to draw time on top of animation frames.
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glDisable(GL_DITHER);
    glDisable(GL_SCISSOR_TEST);
    glDisable(GL_BLEND);
	// 开启2D纹理的配置
    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, 0);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    bool clockFontInitialized = false;
    if (mClockEnabled) {  //一般不配置显示这个时钟,不走这里
        clockFontInitialized =
            (initFont(&mAnimation->clockFont, CLOCK_FONT_ASSET) == NO_ERROR);
        mClockEnabled = clockFontInitialized;
    }

    initFont(&mAnimation->progressFont, PROGRESS_FONT_ASSET);  //初始化字体。用于绘制始终和进度,一般不配置
	...
    playAnimation(*mAnimation);  //真正的播放
	...
    releaseAnimation(mAnimation);
    mAnimation = nullptr;

    return false;
}

最终的播放逻辑在playAnimation

bool BootAnimation::playAnimation(const Animation& animation) {
    const size_t pcount = animation.parts.size();
    nsecs_t frameDuration = s2ns(1) / animation.fps;

    SLOGD("%sAnimationShownTiming start time: %" PRId64 "ms", mShuttingDown ? "Shutdown" : "Boot",
            elapsedRealtime());

    int fadedFramesCount = 0;
    int lastDisplayedProgress = 0;
    int colorTransitionStart = animation.colorTransitionStart;
    int colorTransitionEnd = animation.colorTransitionEnd;
    for (size_t i=0 ; i<pcount ; i++) {
        const Animation::Part& part(animation.parts[i]);
        const size_t fcount = part.frames.size();

        // Handle animation package
        if (part.animation != nullptr) {  //如果存在嵌套动画文件,就递归去播放,一般没有
            playAnimation(*part.animation);
            if (exitPending())
                break;
            continue; //to next part
        }

        // process the part not only while the count allows but also if already fading
        //如果part的count = 0 或者 还没有循环到part的count次数,就继续循环
        for (int r=0 ; !part.count || r<part.count || fadedFramesCount > 0 ; r++) {
            if (shouldStopPlayingPart(part, fadedFramesCount, lastDisplayedProgress)) break; //每次循环进入播放part时检查是否应该退出,判断是否boot完成且part的repeat-type不是c,即p时才能退出
			...
            mCallbacks->playPart(i, part, r); //播放part里面的音频,一般没音频

            glClearColor(
                    part.backgroundColor[0],
                    part.backgroundColor[1],
                    part.backgroundColor[2],
                    1.0f);

            ALOGD("Playing files = %s/%s, Requested repeat = %d, playUntilComplete = %s",
                    animation.fileName.string(), part.path.string(), part.count,
                    part.playUntilComplete ? "true" : "false");

            // For the last animation, if we have progress indicator from
            // the system, display it.
            int currentProgress = android::base::GetIntProperty(PROGRESS_PROP_NAME, 0);
            bool displayProgress = animation.progressEnabled &&
                (i == (pcount -1)) && currentProgress != 0;

            for (size_t j=0 ; j<fcount ; j++) {
                if (shouldStopPlayingPart(part, fadedFramesCount, lastDisplayedProgress)) break;  // 每次循环进入播放每一个图片时检查是否应该退出,判断是否boot完成且part的repeat-type不是c,即p时才能退出
				...
                processDisplayEvents();  //轮询是否有display的回调事件

                const double ratio_w = static_cast<double>(mWidth) / mInitWidth;
                const double ratio_h = static_cast<double>(mHeight) / mInitHeight;
                const int animationX = (mWidth - animation.width * ratio_w) / 2;
                const int animationY = (mHeight - animation.height * ratio_h) / 2;

                const Animation::Frame& frame(part.frames[j]);  //拿到具体part中的某张图片
                nsecs_t lastFrame = systemTime();

                if (r > 0) {
                    glBindTexture(GL_TEXTURE_2D, frame.tid);
                } else {  //如果是第一次播放图片(第一张图片)
                    glGenTextures(1, &frame.tid);  //生成纹理
                    glBindTexture(GL_TEXTURE_2D, frame.tid);  //将图片的句柄和纹理绑定
                    int w, h;
                    // Set decoding option to alpha unpremultiplied so that the R, G, B channels
                    // of transparent pixels are preserved.
                    initTexture(frame.map, &w, &h, false /* don't premultiply alpha */);  //初始化纹理
                }

                const int trimWidth = frame.trimWidth * ratio_w;
                const int trimHeight = frame.trimHeight * ratio_h;
                const int trimX = frame.trimX * ratio_w;
                const int trimY = frame.trimY * ratio_h;
                const int xc = animationX + trimX;
                const int yc = animationY + trimY;
                glClear(GL_COLOR_BUFFER_BIT);
                // specify the y center as ceiling((mHeight - frame.trimHeight) / 2)
                // which is equivalent to mHeight - (yc + frame.trimHeight)
                const int frameDrawY = mHeight - (yc + trimHeight);

                float fade = 0;
				...
                glUseProgram(mImageShader);
                glUniform1i(mImageTextureLocation, 0);
                glUniform1f(mImageFadeLocation, fade);
                if (animation.dynamicColoringEnabled) {
                    glUniform1f(mImageColorProgressLocation, colorProgress);
                }
                glEnable(GL_BLEND);
                drawTexturedQuad(xc, frameDrawY, trimWidth, trimHeight);
                glDisable(GL_BLEND);

				...

                handleViewport(frameDuration);  //处理视口的区域的变化

                eglSwapBuffers(mDisplay, mSurface);  //交换显示buffer,显示出来

                nsecs_t now = systemTime();
                nsecs_t delay = frameDuration - (now - lastFrame);
                //SLOGD("%lld, %lld", 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, nullptr);
                    } while (err == EINTR);
                }

                checkExit();  //每一个帧显示完检查下是否需要退出,具体就是去查询boot是否完成,如果boot完成SF会设置某个系统属性,那么就会设置线程的请求退出的标志
            }

            usleep(part.pause * ns2us(frameDuration)); //播放完本次part了,如果pause != 0 就需要延时(pause * frame时长)的时间再接着往下播放当前part的下一个循环或者下一个part

			//这里的判断很重要,从前面part的for循环和退出判断可知,当part的repeat-type为c时,且count = 0时,
			//即使boot完成了也不会退出循环,会进行播放,但是什么时候退出呢,答案就是下面的判断,如果boot完成了(exitPending() = true)且part = 0, 即part的配置为(c 0 x)时会结束循环,
			//所以可知当配置了part repeat为c时,那么这个part至少会播放一次,如果一个zip里有多个c-part时,
			//每个c-part都至少播放一次,无论它的count是0还是非0,即使boot完成。
            if (exitPending() && !part.count && mCurrentInset >= mTargetInset &&
                !part.hasFadingPhase()) {
                if (lastDisplayedProgress != 0 && lastDisplayedProgress != 100) {
                    android::base::SetProperty(PROGRESS_PROP_NAME, "100");
                    continue;
                }
                break; // exit the infinite non-fading part when it has been played at least once
            }
        }
    }

    // Free textures created for looping parts now that the animation is done.
    for (const Animation::Part& part : animation.parts) {
        if (part.count != 1) {
            const size_t fcount = part.frames.size();
            for (size_t j = 0; j < fcount; j++) {
                const Animation::Frame& frame(part.frames[j]);
                glDeleteTextures(1, &frame.tid);
            }
        }
    }

    ALOGD("%sAnimationShownTiming End time: %" PRId64 "ms", mShuttingDown ? "Shutdown" : "Boot",
            elapsedRealtime());

    return true;
}

void BootAnimation::checkExit() {
    // Allow surface flinger to gracefully request shutdown  //从这里我们也可以知道是SF直接设置这个属性让开机动画退出
    char value[PROPERTY_VALUE_MAX];
    property_get(EXIT_PROP_NAME, value, "0"); //service.bootanim.exit = 1,则调用requestExit()
    int exitnow = atoi(value);
    if (exitnow) {
        requestExit();  //这个是Thread类的方法,调用这个方法后exitPending() = true
    }
}

bool BootAnimation::shouldStopPlayingPart(const Animation::Part& part,
                                          const int fadedFramesCount,
                                          const int lastDisplayedProgress) {
    // stop playing only if it is time to exit and it's a partial part which has been faded out
    return exitPending() && !part.playUntilComplete && fadedFramesCount >= part.framesToFadeCount &&
        (lastDisplayedProgress == 0 || lastDisplayedProgress == 100);  //part.playUntilComplete就是part的repeat type ->c或者p
}

二、结束开机动画

2.1 system server进程

开机动画的结束是开机过程中,系统完成开机完成,即将显示Home应用时调用的,具体是在performEnableScreen方法中
framework/base/services/core/java/com/android/server/wm/WindowManagerService.java

    private void performEnableScreen() {
        synchronized (mGlobalLock) {
            ProtoLog.i(WM_DEBUG_BOOT, "performEnableScreen: mDisplayEnabled=%b"
                            + " mForceDisplayEnabled=%b" + " mShowingBootMessages=%b"
                            + " mSystemBooted=%b mOnlyCore=%b. %s", mDisplayEnabled,
                    mForceDisplayEnabled, mShowingBootMessages, mSystemBooted, mOnlyCore,
                    new RuntimeException("here").fillInStackTrace());
    		...
    		
            if (!mBootAnimationStopped) {
                Trace.asyncTraceBegin(TRACE_TAG_WINDOW_MANAGER, "Stop bootanim", 0);
                // stop boot animation
                // formerly we would just kill the process, but we now ask it to exit so it
                // can choose where to stop the animation.
                SystemProperties.set("service.bootanim.exit", "1");  //设置开机动画退出属性,开机动画播放线程循环中会去check这个属性是否设置为1,然后请求退出循环
                mBootAnimationStopped = true;
            }

            if (!mForceDisplayEnabled && !checkBootAnimationCompleteLocked()) {
                ProtoLog.i(WM_DEBUG_BOOT, "performEnableScreen: Waiting for anim complete");
                return;
            }

            try {
            	//跨进程通知SF去处理让开机动画退出
                IBinder surfaceFlinger = ServiceManager.getService("SurfaceFlinger");
                if (surfaceFlinger != null) {
                    ProtoLog.i(WM_ERROR, "******* TELLING SURFACE FLINGER WE ARE BOOTED!");
                    Parcel data = Parcel.obtain();
                    data.writeInterfaceToken("android.ui.ISurfaceComposer");
                    surfaceFlinger.transact(IBinder.FIRST_CALL_TRANSACTION, // BOOT_FINISHED
                            data, null, 0);  //注意这个IBinder.FIRST_CALL_TRANSACTION,这是system server第一次和SF binder通信,SF当收到是这个FLAG时处理通知关闭开机动画
                    data.recycle();
                }
            } catch (RemoteException ex) {
                ProtoLog.e(WM_ERROR, "Boot completed: SurfaceFlinger is dead!");
            }

            EventLogTags.writeWmBootAnimationDone(SystemClock.uptimeMillis());
            Trace.asyncTraceEnd(TRACE_TAG_WINDOW_MANAGER, "Stop bootanim", 0);
            mDisplayEnabled = true;
            ProtoLog.i(WM_DEBUG_SCREEN_ON, "******************** ENABLING SCREEN!");

            // Enable input dispatch.
            mInputManagerCallback.setEventDispatchingLw(mEventDispatchingEnabled);  //开始使能input dispatch 可以开始分发事件
        }

        try {
            mActivityManager.bootAnimationComplete(); // 告知AMS开机动画已经完成
        } catch (RemoteException e) {
        }

        mPolicy.enableScreenAfterBoot();

        // Make sure the last requested orientation has been applied.
        updateRotationUnchecked(false, false);
    }

2.2 SurfaceFlinger进程

WMS跨进程调用到SurfaceFlinger::onTransact中处理
frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp

status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
                                    uint32_t flags) {
    if (const status_t error = CheckTransactCodeCredentials(code); error != OK) {
        return error;
    }

    status_t err = BnSurfaceComposer::onTransact(code, data, reply, flags);  //由SurfaceFlinger父类处理    ...
}

frameworks/native/libs/gui/include/gui/ISurfaceComposer.h

class BnSurfaceComposer: public BnInterface<ISurfaceComposer> {
public:
    enum ISurfaceComposerTag {
        // Note: BOOT_FINISHED must remain this value, it is called from
        // Java by ActivityManagerService.
        BOOT_FINISHED = IBinder::FIRST_CALL_TRANSACTION,  //远程调用的FLAG, 实际上是BOOT_FINISHED
		...
    };

    virtual status_t onTransact(uint32_t code, const Parcel& data,
            Parcel* reply, uint32_t flags = 0);
};

frameworks/native/libs/gui/ISurfaceComposer.cpp

status_t BnSurfaceComposer::onTransact(  //由上面可知,在父类中处理BOOT_FINISHED
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    switch(code) {
		...
        case BOOT_FINISHED: {
            CHECK_INTERFACE(ISurfaceComposer, data, reply);
            bootFinished();  //父类中的纯虚函数需要子类实现,所以会调用SF子类的实现
            return NO_ERROR;
        }

frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
父类中的纯虚函数需要子类实现,所以会调用SF的实现

void SurfaceFlinger::bootFinished() {
	...
    // stop boot animation
    // formerly we would just kill the process, but we now ask it to exit so it
    // can choose where to stop the animation.
    property_set("service.bootanim.exit", "1");  //设置service.bootanim.exit -> 1 告知开机动画进程退出

    const int LOGTAG_SF_STOP_BOOTANIM = 60110;
    LOG_EVENT_LONG(LOGTAG_SF_STOP_BOOTANIM,
                   ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));

    sp<IBinder> input(defaultServiceManager()->getService(String16("inputflinger")));
	...
}

到这里退出开机动画的时间点和动作就了解清楚了。开机动画基本已经分析结束。文章来源地址https://www.toymoban.com/news/detail-494555.html

到了这里,关于[Android 13]开机动画原理分析的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Android动画深入分析(View动画)

    Android的动画我其实在View的滑动里面写过,主要还是分为2点。 一个就是View动画,还有一个是属性动画 先讲述 View动画 View动画主要分为4种, 平移动画 , 缩放动画 , 旋转动画 , 透明度动画 。 还有一个叫 帧动画 ,但是表现方式和上面4种不太一样 一般情况下我们是在xml里面定义动

    2024年02月07日
    浏览(65)
  • Android13 wifi状态问题分析

    场景; 设置wif 默认开关属性: 系统升级后,wifi 默认设置属性无效。 原因: 多个地方存在定义默认wifi开关状态的定义。 解决方法: 可能是配置文件,有overlay,一般是在 vendor 下面有 WifiOverlay的情况,或者全局查找,使用命令查找: 看看系统源码哪里还会设置默认状态的

    2024年02月01日
    浏览(34)
  • Android13音频子系统分析(一)---整体架构

             目录 一、应用API层 二、Java框架层 三、Native核心层 3.1 AudioFlinger模块 3.2 AudioPolicyService模块 四、HAL层         本文基于AOSP13源码进行分析解读。所以与各个SoC平台厂商提供的运行在真实设备上的源码会有细微差异,但核心原理区别不大。         音频子系

    2024年02月07日
    浏览(48)
  • Android 13 网络 Adb相关流程深入分析研究

    通过代码分析发现Android13 上对 网络adb 进行了限制! Android13原生代码要求:必现连接上某个wifi,才能进行adb ,并且切换wifi或者关闭wifi都是会停止adb。 并且Android13 上 wifi adb 端口号每次是变化的,这个也是很不方便的! 如果要做成Android11 或者之前一样,设备连接WiFi/有线网

    2024年02月09日
    浏览(50)
  • Android13音频子系统分析(二)---初始化

    目录 一、AudioPolicyService初始化 1.1 AudioPolicyService::onFirstRef()函数 1.2 AudioCommandThread线程 1.3 AudioPolicyManager初始化 1.3.1 解析audio_policy_configuration.xml配置文件 1.3.2 解析audio_policy_engine_configuration.xml配置文件 1.3.3 AudioPolicyManager::onNewAudioModulesAvailableInt()函数 1.3.4 AudioPolicyManager::updateDe

    2024年02月03日
    浏览(43)
  • Android13冻结进程分析:如何提高设备性能和用户体验

    本文介绍了Android13中的冻结进程功能,它是一种重要的资源管理策略,可以提高系统性能和稳定性,同时最大限度地节省设备的资源和电池消耗。 文章讨论了如何合理分配资源,包括CPU、内存等,以提高设备性能和用户体验。此外,文章还提到了冻结进程对应用程序线程的影

    2024年02月14日
    浏览(49)
  • Android13音频子系统分析(三)---音效算法集成框架

    目录 一、Android音效C/S架构 二、EffectHAL音效框架的初始化与使用 2.1音效框架初始化 2.2创建并加载音效算法 2.3执行音效算法 三、AudioFlinger对音效框架的二次封装 四、Device音效的绑定过程 4.1 DeviceHAL处理音效数据的方式 4.2 AudioFlinger处理音效数据的方式 五、Stream音效的绑定过程

    2024年02月03日
    浏览(42)
  • 基于Android13的系统启动流程分析(四)之SecondStageMain阶段

    Android13系统启动阶段大致分为FirstStageMain阶段和SecondStageMain,此章主要讲SecondStageMain阶段 (若分析有误敬请指教) 在基于Android13的系统启动流程分析(三)之FirstStageMain阶段已经讲解过android系统启动的基本介绍了,这里不再单独介绍了 我们先看是怎么进入该阶段的,仍然是

    2023年04月24日
    浏览(45)
  • 基于Android13的系统启动流程分析(三)之FirstStageMain阶段

    Android13系统启动阶段大致分为FirstStageMain阶段和SecondStageMain,此章主要讲FirstStageMain阶段 (若分析有误敬请指教) 本章讲解的方向和你将收获的知识: 用户空间进程的调用流程 当进程挂掉后该如何处理 何时挂载上的基本文件系统和文件系统小知识 FirstStageMain阶段会挂载上什

    2024年02月10日
    浏览(52)
  • 基于Android13的系统启动流程分析(一)之SeLinux权限介绍

    学习Android系统启动流程之前先学习一下SeLinux权限系统,步入正题 本章讲解的方向和你将收获的知识: 什么是SeLinux系统,SeLinux的简介和介绍 SeLinux系统的主要作用和存在的意义,是基于哪个版本开始推行该方案的 如果遇到了SeLinux权限问题该如何解决,有几种解决方案 SeLi

    2024年02月04日
    浏览(87)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包