Android 开机动画启动、播放、退出流程(android 10)

本文详细解析了Android 10系统的开机动画启动、播放和退出的整个流程,涉及SurfaceFlinger进程、StartPropertySetThread线程、属性服务、bootanimation服务以及开机动画的加载和绘制过程。在开机动画启动时,init进程启动surfaceflinger,通过设置属性控制动画开始。在SurfaceFlinger中,BootAnimation类负责播放动画,当“service.bootanim.exit”属性为0时,动画退出。

Android 开机动画启动流程 (android 10)

1 开机动画启动流程

我们先来看一下开机动画是如何启动,并开始播放的。

通过系统启动流程分析可以得知,在系统内核启动后,会启动第一个init进程,init进程会扫描、解析init.rc文件,在init.rc文件中,会启动 surfaceflinger 进程, 在surfaceflinger 进程的main函数中会进行 SurfaceFlinger binder服务的启动,开机动画的相关流程正是在其中进行的,我们下面将会进行分析。

Android.bp 文件

# frameworks/native/services/surfaceflinger/Android.bp
filegroup {
    name: "surfaceflinger_binary_sources",
    srcs: ["main_surfaceflinger.cpp"],
}
cc_binary {
    name: "surfaceflinger",
    defaults: ["libsurfaceflinger_binary"],
    # rc 文件引入,会将 surfaceflinger.rc 文件拷贝到指定目录进行解析
    init_rc: ["surfaceflinger.rc"],
    srcs: [":surfaceflinger_binary_sources"],
    shared_libs: [
        "libsurfaceflinger",
        "libSurfaceFlingerProp",
    ],
}
//...

surfaceflinger.rc 文件

# frameworks/native/services/surfaceflinger/surfaceflinger.rc
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函数入口的相关。

  • surfaceflinger::createSurfaceFlinger
  • flinger->init()
  • sm->addService(SurfaceFlinger::getServiceName())
  • flinger->run()
// frameworks/native/services/surfaceflinger/main_surfaceflinger.cpp
int main(int, char**) {
    signal(SIGPIPE, SIG_IGN);

    hardware::configureRpcThreadpool(1 /* maxThreads */, false /* callerWillJoin */);

    startGraphicsAllocatorService();
		// 当 surfaceflinger 在自己的进程启动时,限制 binder 线程的数量
    ProcessState::self()->setThreadPoolMaxThreadCount(4);

  	// 启动线程池
    sp<ProcessState> ps(ProcessState::self());
    ps->startThreadPool();

  	// 实例化 surfaceflinger
    sp<SurfaceFlinger> flinger = surfaceflinger::createSurfaceFlinger();

    setpriority(PRIO_PROCESS, 0, PRIORITY_URGENT_DISPLAY);

    //...

    // 在客户端可以连接前,进行一些SurfaceFlinger的初始化操作
    flinger->init();

  	// 添加 SurfaceFlinger 服务到bincer
    sp<IServiceManager> sm(defaultServiceManager());
    sm->addService(String16(SurfaceFlinger::getServiceName()), flinger, false,
                   IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL | IServiceManager::DUMP_FLAG_PROTO);

    startDisplayService(); // dependency on SF getting registered above

    struct sched_param param = {0};
    param.sched_priority = 2;
    if (sched_setscheduler(0, SCHED_FIFO, &param) != 0) {
        ALOGE("Couldn't set SCHED_FIFO");
    }

    // 在当前线程运行 surface flinger
    flinger->run();

    return 0;
}
  • SurfaceFlinger::init()
    getFactory().createStartPropertySetThread
    mStartPropertySetThread->Start()
// 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...");

    //...
    
    // initialize our drawing state
    mDrawingState = mCurrentState;

    // set initial conditions (e.g. unblank default device)
    initializeDisplays();

    getRenderEngine().primeCache();

    // 创建“属性设置线程”并启动,初始化graphics之后,mStartPropertySetThread() 会进行开机动画的播放
  	// 创建 StartPropertySetThread 实例,实际上SurfaceFlinger类里提供了创建实例的方法
    const bool presentFenceReliable =
            !getHwComposer().hasCapability(HWC2::Capability::PresentFenceIsNotReliable);
    mStartPropertySetThread = getFactory().createStartPropertySetThread(presentFenceReliable);

    if (mStartPropertySetThread->Start() != NO_ERROR) {
        ALOGE("Run StartPropertySetThread failed!");
    }
    ALOGV("Done initializing");
}

我们再来看一下 StartPropertySetThread 这个线程做了一些什么操作。

StartPropertySetThread 类继承了 Thread 类,拥有线程的能力,在threadLoop函数中通过设置属性来控制开机动画的开始 。

// frameworks/native/services/surfaceflinger/StartPropertySetThread.cpp
//#include <utils/Thread.h>
namespace android {
StartPropertySetThread::StartPropertySetThread(bool timestampPropertyValue):
        Thread(false), mTimestampPropertyValue(timestampPropertyValue) {}

status_t StartPropertySetThread::Start() {
  	// 调用这里后,会回调到 threadLoop
  	// 这里使用了封装线程pthread_t的库,见system/core/libutils/include/utils/Thread.h,后面有机会会分析
    return run("SurfaceFlinger::StartPropertySetThread", PRIORITY_NORMAL);
}

// 线程执行循环 threadLoop函数,根据返回值来判断是否继续循环, false 则退出循环
bool StartPropertySetThread::threadLoop() {
    // Set property service.sf.present_timestamp, consumer need check its readiness
    property_set(kTimestampProperty, mTimestampPropertyValue ? "1" : "0");
  	// 清除开机动画退出标志
    property_set("service.bootanim.exit", "0");
  	// 通过设置属性来启动开机动画
    property_set("ctl.start", "bootanim");
  	// 返回 false, 则立即退出线程
    return false;
}
} // namespace android

为什么设置了属性后开机动画就能开始执行呢,原因是有地方监听了这个属性变化,在 init 进程启动的时候,system/core/init/main.cpp的main函数中启动了一个属性服务, 用于处理系统属性的变化。

# system/core/init/Android.bp
cc_binary {
    name: "init_second_stage",
    recovery_available: true,
    stem: "init",
    defaults: ["init_defaults"],
    static_libs: ["libinit"],
    required: [
        "e2fsdroid",
        "mke2fs",
        "sload_f2fs",
        "make_f2fs",
    ],
    # main 函数入口
    srcs: ["main.cpp"],
    symlinks: ["ueventd"],
    target: {
        recovery: {
            cflags: ["-DRECOVERY"],
            exclude_shared_libs: ["libbinder", "libutils"],
        },
    },
    ldflags: ["-Wl,--rpath,/system/${LIB}/bootstrap"],
}

init 进程 main 函数入口

// system/core/init/main.cpp
int main(int argc, char** argv) {
#if __has_feature(address_sanitizer)
    __asan_set_error_report_callback(AsanReportCallback);
#endif

    if (!strcmp(basename(argv[0]), "ueventd")) {
        return ueventd_main(argc, argv);
    }

    if (argc > 1) {
        if (!strcmp(argv[1], "subcontext")) {
            android::base::InitLogging(argv, &android::base::KernelLogger);
            const BuiltinFunctionMap function_map;

            return SubcontextMain(argc, argv, &function_map);
        }

        if (!strcmp(argv[1], "selinux_setup")) {
          	// selinux_setup
            return SetupSelinux(argv);
        }

        if (!strcmp(argv[1], "second_stage")) {
          	// second_stage 阶段,在这里启动了属性服务
            return SecondStageMain(argc, argv);
        }
    }
		// FirstStageMain 阶段,主要用于挂载系统基本的磁盘文件
    return FirstStageMain(argc, argv);
}
  • SecondStageMain
    • property_init()
int SecondStageMain(int argc, char** argv) {
    if (REBOOT_BOOTLOADER_ON_PANIC) {
        InstallRebootSignalHandlers();
    }

    SetStdioToDevNull(argv);
  	// 初始化Kernel内核日志
    InitKernelLogging(argv);
    LOG(INFO) << "init second stage started!";

  	// 设置 init 进程及其子进程的 oom_adj -1000
    if (auto result = WriteFile("/proc/1/oom_score_adj", "-1000"); !result) {
        LOG(ERROR) << "Unable to write -1000 to /proc/1/oom_score_adj: " << result.error();
    }

    // 省略代码。。。

    // Indicate that booting is in progress to background fw loaders, etc.
    close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
		// 属性初始化,从文件中解析系统属性,并存储到 property_infos 集合中,过滤集合中属性再将其写入 /dev/__properties__/property_info文件中
    property_init();

    // If arguments are passed both on the command line and in DT,
    // properties set in DT always have priority over the command-line ones.
    process_kernel_dt();
    process_kernel_cmdline();

    // Propagate the kernel variables to internal variables
    // used by init as well as the current required properties.
    export_kernel_boot_props();

    // Make the time that init started available for bootstat to log.
    property_set("ro.boottime.init", getenv("INIT_STARTED_AT"));
    property_set("ro.boottime.init.selinux", getenv("INIT_SELINUX_TOOK"));

    // ...

  	// 设置 SELinux 
    // Now set up SELinux for second stage.
    SelinuxSetupKernelLogging();
    SelabelInitialize();
    SelinuxRestoreContext();

    Epoll epoll;
    if (auto result = epoll.Open(); !result) {
        PLOG(FATAL) << result.error();
    }

    InstallSignalFdHandler(&epoll);

    property_load_boot_defaults(load_debug_prop);
    UmountDebugRamdisk();
    fs_mgr_vendor_overlay_mount_all();
    export_oem_lock_status();
  	// 启动属性服务 PropertyService
    StartPropertyService(&epoll);
    MountHandler mount_handler(&epoll);
    set_usb_controller();

    const BuiltinFunctionMap function_map;
    Action::set_function_map(&function_map);

    if (!SetupMountNamespaces()) {
        PLOG(FATAL) << "SetupMountNamespaces failed";
    }

    subcontexts = InitializeSubcontexts();

    ActionManager& am = ActionManager::GetInstance();
    ServiceList& sm = ServiceList::GetInstance();

  	// 加载 BootScripts
    LoadBootScripts(am, sm);

    //...

    return 0;
}

启动属性服务 PropertyService, 这里使用epoll机制来监听属性变化

  • StartPropertyService
    • CreateSocket
    • listen(property_set_fd, 8)
    • epoll->RegisterHandler
// system/core/init/property_service.cpp
void StartPropertyService(Epoll* epoll) {
    selinux_callback cb;
    cb.func_audit = SelinuxAuditCallback;
    selinux_set_callback(SELINUX_CB_AUDIT, cb);

    property_set("ro.property_service.version", "2");
		// 创建 Socket
    property_set_fd = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, false, 0666, 0, 0, nullptr);
    if (property_set_fd == -1) {
        PLOG(FATAL) << "start_property_service socket creation failed";
    }
	  // 监听
    listen(property_set_fd, 8);
		// 注册属性变化处理 handle_property_set_fd
    if (auto result = epoll->RegisterHandler(property_set_fd, handle_property_set_fd); !result) {
        PLOG(FATAL) << result.error();
    }
}

在这个回调函数中处理属性设置的监听

  • handle_property_set_fd
static void handle_property_set_fd() {
    static constexpr uint32_t kDefaultSocketTimeout = 2000; /* ms */
		// 接收连接 property_set_fd
    int s = accept4(property_set_fd, nullptr, nullptr, SOCK_CLOEXEC);
    if (s == -1) {
        return;
    }

    ucred cr;
    socklen_t cr_size = sizeof(cr);
    if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cr_size) < 0) {
        close(s);
        PLOG(ERROR) << "sys_prop: unable to get SO_PEERCRED";
        return;
    }

    SocketConnection socket(s, cr);
    uint32_t timeout_ms = kDefaultSocketTimeout;

    uint32_t cmd = 0;
    if (!socket.RecvUint32(&cmd, &timeout_ms)) {
        PLOG(ERROR) << "sys_prop: error while reading command from the socket";
        socket.SendUint32(PROP_ERROR_READ_CMD);
        return;
    }

    switch (cmd) {
    // 属性设置的消息处理
    case PROP_MSG_SETPROP: {
        char prop_name[PROP_NAME_MAX];
        char prop_value[PROP_VALUE_MAX];

        if (!socket.RecvChars(prop_name, PROP_NAME_MAX, &timeout_ms) ||
            !socket.RecvChars(prop_value, PROP_VALUE_MAX, &timeout_ms)) {
          PLOG(ERROR) << "sys_prop(PROP_MSG_SETPROP): error while reading name/value from the socket";
          return;
        }

        prop_name[PROP_NAME_MAX-1] = 0;
        prop_value[PROP_VALUE_MAX-1] = 0;

        const auto& cr = socket.cred();
        std::string error;
      	// 处理属性设置
        uint32_t result = HandlePropertySet(prop_name, prop_value, socket.source_context(), cr, &error);
        if (result != PROP_SUCCESS) {
            LOG(ERROR) << "Unable to set property '" << prop_name << "' to '" << prop_value
                       << "' from uid:" << cr.uid << " gid:" << cr.gid << " pid:" << cr.pid << ": "
                       << error;
        }

        break;
      }

    case PROP_MSG_SETPROP2: {
        std::string name;
        std::string value;
        if (!socket.RecvString(&name, &timeout_ms) ||
            !socket.RecvString(&value, &timeout_ms)) {
          PLOG(ERROR) << "sys_prop(PROP_MSG_SETPROP2): error while reading name/value from the socket";
          socket.SendUint32(PROP_ERROR_READ_DATA);
          return;
        }

        const auto& cr = socket.cred();
        std::string error;
        uint32_t result = HandlePropertySet(name, value, socket.source_context(), cr, &error);
        if (result != PROP_SUCCESS) {
            LOG(ERROR) << "Unable to set property '" << name << "' to '" << value
                       << "' from uid:" << cr.uid << " gid:" << cr.gid << " pid:" << cr.pid << ": "
                       << error;
        }
        socket.SendUint32(result);
        break;
      }

    default:
        LOG(ERROR) << "sys_prop: invalid command " << cmd;
        socket.SendUint32(PROP_ERROR_INVALID_CMD);
        break;
    }
}
  • HandlePropertySet

处理传过来的属性、属性值,“ctl.” 开头的会特殊处理。

uint32_t HandlePropertySet(const std::string& name, const std::string& value,
                           const std::string& source_context, const ucred& cr, std::string* error) {
    if (auto ret = CheckPermissions(name, value, source_context, cr, error); ret != PROP_SUCCESS) {
        return ret;
    }
		// 在前面我们见过在 surfaceflinger 启动属性设置线程时,会设置相关的属性,即 property_set("ctl.start", "bootanim");
    if (StartsWith(name, "ctl.")) {
      	// 继续跟进,传入参数 ("start","bootanim",cr.pid)
        HandleControlMessage(name.c_str() + 4, value, cr.pid);
        return PROP_SUCCESS;
    }
    if (name == "sys.powerctl") {
        std::string cmdline_path = StringPrintf("proc/%d/cmdline", cr.pid);
        std::string process_cmdline;
        std::string process_log_string;
        if (ReadFileToString(cmdline_path, &process_cmdline)) {
            process_log_string = StringPrintf(" (%s)", process_cmdline.c_str());
        }
        LOG(INFO) << "Received sys.powerctl='" << value << "' from pid: " << cr.pid
                  << process_log_string;
    }

    if (name == "selinux.restorecon_recursive") {
        return PropertySetAsync(name, value, RestoreconRecursiveAsync, error);
    }

    return PropertySet(name, value, error);
}
  • HandleControlMessage

不同的消息都有不同的处理方式, start 对应的处理类型为 ControlTarget::SERVICE,回调函数为 DoControlStart,通过传进来的 bootanim 来找到开机动画服务,这个服务是在 init 进程启动时,扫描 rc文件来将服务添加到服务列表中,可知也会有一处地方是对 bootanim 有进行配置的。

// system/core/init/init.cpp
void HandleControlMessage(const std::string& msg, const std::string& name, pid_t pid) {
  	// 不同的消息都有不同的处理方式,ControlTarget::SERVICE 对应 start
  	// {"start", {ControlTarget::SERVICE,   DoControlStart}}
    const auto& map = get_control_message_map();
    const auto it = map.find(msg);

    if (it == map.end()) {
        LOG(ERROR) << "Unknown control msg '" << msg << "'";
        return;
    }

    std::string cmdline_path = StringPrintf("proc/%d/cmdline", pid);
    std::string process_cmdline;
    if (ReadFileToString(cmdline_path, &process_cmdline)) {
        std::replace(process_cmdline.begin(), process_cmdline.end(), '\0', ' ');
        process_cmdline = Trim(process_cmdline);
    } else {
        process_cmdline = "unknown process";
    }

    const ControlMessageFunction& function = it->second;

    Service* svc = nullptr;

    switch (function.target) {
        // SERVICE 类型
        case ControlTarget::SERVICE:
        		// 在这里通过传进来的 name 参数, 即 “bootanima”,找到对应的服务,然后调用 DoControlStart 方法
            svc = ServiceList::GetInstance().FindService(name);
            break;
        case ControlTarget::INTERFACE:
            svc = ServiceList::GetInstance().FindInterface(name);
            break;
        default:
            LOG(ERROR) << "Invalid function target from static map key '" << msg << "': "
                       << static_cast<std::underlying_type<ControlTarget>::type>(function.target);
            return;
    }

    if (svc == nullptr) {
        LOG(ERROR) << "Could not find '" << name << "' for ctl." << msg;
        return;
    }

    if (auto result = function.action(svc); !result) {
        LOG(ERROR) << "Could not ctl." << msg << " for '" << name << "': " << result.error();
    }
}

消息处理方式的定义

  • get_control_message_map

static const std::map<std::string, ControlMessageFunction>& get_control_message_map() {
    // clang-format off
    static const std::map<std::string, ControlMessageFunction> control_message_functions = {
        {"sigstop_on",        {ControlTarget::SERVICE,
                               [](auto* service) { service->set_sigstop(true); return Success(); }}},
        {"sigstop_off",       {ControlTarget::SERVICE,
                               [](auto* service) { service->set_sigstop(false); return Success(); }}},
        {"start",             {ControlTarget::SERVICE,   DoControlStart}},
        {"stop",              {ControlTarget::SERVICE,   DoControlStop}},
        {"restart",           {ControlTarget::SERVICE,   DoControlRestart}},
        {"interface_start",   {ControlTarget::INTERFACE, DoControlStart}},
        {"interface_stop",    {ControlTarget::INTERFACE, DoControlStop}},
        {"interface_restart", {ControlTarget::INTERFACE, DoControlRestart}},
    };
    return control_message_functions;
}

2 开机动画的启动

分析了上面的属性处理,我们看一下,我们来看一下 bootanimation 的定义

我们通过 grep “service bootanim” -rn ./ 脚本查找的方式来定位 rc 文件位置。

# frameworks/base/cmds/bootanimation/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

通过 /system/bin/bootanimation 可知,bootanimation 是一个可编译模块,当我们找不到源码位置时,可以通过 grep 在源码中进行查找,如 grep “bootanimation” -rn ./ ,我们定位到以下 Android.bp 文件:

//frameworks/base/cmds/bootanimation/Android.bp
cc_binary {
    name: "bootanimation",
    defaults: ["bootanimation_defaults"],

    shared_libs: [
        "libOpenSLES",
        "libbootanimation",
    ],

    srcs: [
        "BootAnimationUtil.cpp",
      	# main 函数入口
        "bootanimation_main.cpp",
        "audioplay.cpp",
    ],

    //...
		// rc文件入口
    init_rc: ["bootanim.rc"],
}

直接来看一下 bootanimation_main.cpp 的main函数

// 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();
				
      	// 实例化 BootAnimation 对象(may take up to 200ms for 2MB zip)
        sp<BootAnimation> boot = new BootAnimation(audioplay::createAnimationCallbacks());
				// 等待 SurfaceFlinger 服务启动再进行下面的操作
        waitForSurfaceFlinger();
      	// BootAnimation 继承于 Thread 类,拥有线程的能力,调用 run 方法直接启动线程
        boot->run("BootAnimation", PRIORITY_DISPLAY);
        ALOGV("Boot animation set up. Joining pool.");
      	// 启动binder线程池,后面与surfaceflinger通信会用到
        IPCThreadState::self()->joinThreadPool();
    }
    return 0;
}

等待 SurfaceFlinger 服务启动再进行开机动画的播放

  • waitForSurfaceFlinger
void waitForSurfaceFlinger() {
    // TODO: replace this with better waiting logic in future, b/35253872
    int64_t waitStartTime = elapsedRealtime();
    // 获取 BpServiceManager,这里也是一个 binder 代理对象
    sp<IServiceManager> sm = defaultServiceManager();
    const String16 name("SurfaceFlinger");
    const int SERVICE_WAIT_SLEEP_MS = 100;
    const int LOG_PER_RETRIES = 10;
    int retry = 0;
  	// 检查SurfaceFlinger服务是否启动
    while (sm->checkService(name) == nullptr) {
        retry++;
        if ((retry % LOG_PER_RETRIES) == 0) {
            ALOGW("Waiting for SurfaceFlinger, waited for %" PRId64 " ms",
                  elapsedRealtime() - waitStartTime);
        }
     		// 休眠,const int SERVICE_WAIT_SLEEP_MS = 100;
        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);
    }
}

3 开机动画播放流程

在 BootAnimation 对象创建时, 会回调 BootAnimation::onFirstRef()

  • onFirstRef
// frameworks/base/cmds/bootanimation/BootAnimation.cpp
void BootAnimation::onFirstRef() {
    status_t err = mSession->linkToComposerDeath(this);
    SLOGE_IF(err, "linkToComposerDeath failed (%s) ", strerror(-err));
    if (err == NO_ERROR) {
      	// 加载开机动画
        preloadAnimation();
        ALOGD("%sAnimationPreloadStopTiming start time: %" PRId64 "ms",
                mShuttingDown ? "Shutdown" : "Boot", elapsedRealtime());
    }
}
  • preloadAnimation
    • findBootAnimationFile
    • loadAnimation
bool BootAnimation::preloadAnimation() {
    findBootAnimationFile();
    if (!mZipFileName.isEmpty()) {
        mAnimation = loadAnimation(mZipFileName);
        return (mAnimation != nullptr);
    }
    return false;
}
  • findBootAnimationFile

查找开机动画的文件位置,这里可以进行一些客制化需求的修改

void BootAnimation::findBootAnimationFile() {
    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 char* encryptedBootFiles[] =
            {PRODUCT_ENCRYPTED_BOOTANIMATION_FILE, SYSTEM_ENCRYPTED_BOOTANIMATION_FILE};
        for (const char* f : encryptedBootFiles) {
            if (access(f, R_OK) == 0) {
                mZipFileName = f;
                return;
            }
        }
    }

    const bool playDarkAnim = android::base::GetIntProperty("ro.boot.theme", 0) == 1;
    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, ""};

    for (const char* f : (!mShuttingDown ? bootFiles : shutdownFiles)) {
        if (access(f, R_OK) == 0) {
            mZipFileName = f;
            return;
        }
    }
}
  • loadAnimation

从文件中加载开机动画

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

    Animation *animation =  new Animation;
    animation->fileName = fn;
    animation->zip = zip;
    animation->clockFont.map = nullptr;
    mLoadedFiles.add(animation->fileName);

    parseAnimationDesc(*animation);
    if (!preloadZip(*animation)) {
        return nullptr;
    }
    mLoadedFiles.remove(fn);
    return animation;
}

由于 BootAnimation 继承于Thread, 所以我们可以看一下该类中相关的一些方法。

readyToRun 在线程启动时会被调用一次,可以用作资源一次初始化的地方。

  • readyToRun
status_t BootAnimation::readyToRun() {
    mAssets.addDefaultAssets();

    mDisplayToken = SurfaceComposerClient::getInternalDisplayToken();
    if (mDisplayToken == nullptr)
        return -1;

    DisplayInfo dinfo;
    status_t status = SurfaceComposerClient::getDisplayInfo(mDisplayToken, &dinfo);
    if (status)
        return -1;
		// 创建 native surface
    sp<SurfaceControl> control = session()->createSurface(String8("BootAnimation"),
            dinfo.w, dinfo.h, PIXEL_FORMAT_RGB_565);

    SurfaceComposerClient::Transaction t;
    t.setLayer(control, 0x40000000)
        .apply();

    sp<Surface> s = control->getSurface();
		// 初始化 opengl、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);
		// egl 使用重点
    eglInitialize(display, nullptr, nullptr);
    eglChooseConfig(display, attribs, &config, 1, &numConfigs);
    surface = eglCreateWindowSurface(display, config, s.get(), nullptr);
    context = eglCreateContext(display, config, nullptr, nullptr);
    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;
    mTargetInset = -1;

    return NO_ERROR;
}

threadLoop 则作为线程中循环的函数,通过返回值判断是否退出该线程。

  • threadLoop
bool BootAnimation::threadLoop()
{
    bool r;
    if (mZipFileName.isEmpty()) {
      	// 通过 opengl 方式绘制,我们这里只分析这种方式
        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;
}

这里是调用 opengl api 绘制的过程,我们重点关注流程,以及其中判断退出开机动画的条件

  • android()
    • eglSwapBuffers()
    • checkExit()
bool BootAnimation::android()
{
    initTexture(&mAndroid[0], mAssets, "images/android-logo-mask.png");
    initTexture(&mAndroid[1], mAssets, "images/android-logo-shine.png");

    mCallbacks->init({});

    // 清屏
    glShadeModel(GL_FLAT);
    glDisable(GL_DITHER);
    glDisable(GL_SCISSOR_TEST);
    glClearColor(0,0,0,1);
    glClear(GL_COLOR_BUFFER_BIT);
    eglSwapBuffers(mDisplay, mSurface);

    glEnable(GL_TEXTURE_2D);
    glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);

    const GLint xc = (mWidth  - mAndroid[0].w) / 2;
    const GLint yc = (mHeight - mAndroid[0].h) / 2;
    const Rect updateRect(xc, yc, xc + mAndroid[0].w, yc + mAndroid[0].h);

    glScissor(updateRect.left, mHeight - updateRect.bottom, updateRect.width(),
            updateRect.height());

    // Blend state
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);

    const nsecs_t startTime = systemTime();
    do {
        nsecs_t now = systemTime();
        double time = now - startTime;
        float t = 4.0f * float(time / us2ns(16667)) / mAndroid[1].w;
        GLint offset = (1 - (t - floorf(t))) * mAndroid[1].w;
        GLint x = xc - offset;

        glDisable(GL_SCISSOR_TEST);
        glClear(GL_COLOR_BUFFER_BIT);

        glEnable(GL_SCISSOR_TEST);
        glDisable(GL_BLEND);
        glBindTexture(GL_TEXTURE_2D, mAndroid[1].name);
        glDrawTexiOES(x,                 yc, 0, mAndroid[1].w, mAndroid[1].h);
        glDrawTexiOES(x + mAndroid[1].w, yc, 0, mAndroid[1].w, mAndroid[1].h);

        glEnable(GL_BLEND);
        glBindTexture(GL_TEXTURE_2D, mAndroid[0].name);
        glDrawTexiOES(xc, yc, 0, mAndroid[0].w, mAndroid[0].h);

        EGLBoolean res = eglSwapBuffers(mDisplay, mSurface);
        if (res == EGL_FALSE)
            break;

        // 12fps: don't animate too fast to preserve CPU
        const nsecs_t sleepTime = 83333 - ns2us(systemTime() - now);
        if (sleepTime > 0)
            usleep(sleepTime);
				// 检查退出
        checkExit();
    } while (!exitPending());

    glDeleteTextures(1, &mAndroid[0].name);
    glDeleteTextures(1, &mAndroid[1].name);
    return false;
}

3 开机动画退出流程

在开机动画进行绘制的过程中,可以发现它会一直检查 “service.bootanim.exit” 这个系统属性值是否为0,来作为退出开机动画的条件,我们可以通过 grep 来查询设置这个属性的地方。

  • checkExit()
// frameworks/base/cmds/bootanimation/BootAnimation.cpp
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();
        mCallbacks->shutdown();
    }
}

到这里开机动画就会成功退出,还要来看一下是谁设置了属性让开机动画退出,主要找到了两个地方:

WMS

private void performEnableScreen() {
    synchronized (mGlobalLock) {
        if (DEBUG_BOOT) Slog.i(TAG_WM, "performEnableScreen: mDisplayEnabled=" + mDisplayEnabled
                + " mForceDisplayEnabled=" + mForceDisplayEnabled
                + " mShowingBootMessages=" + mShowingBootMessages
                + " mSystemBooted=" + mSystemBooted
                + " mOnlyCore=" + mOnlyCore,
                new RuntimeException("here").fillInStackTrace());
        if (mDisplayEnabled) {
            return;
        }
        if (!mSystemBooted && !mShowingBootMessages) {
            return;
        }

        if (!mShowingBootMessages && !mPolicy.canDismissBootAnimation()) {
            return;
        }

        // Don't enable the screen until all existing windows have been drawn.
        if (!mForceDisplayEnabled
                // TODO(multidisplay): Expand to all displays?
                && getDefaultDisplayContentLocked().checkWaitingForWindows()) {
            return;
        }

        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");
            mBootAnimationStopped = true;
        }

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

        try {
            IBinder surfaceFlinger = ServiceManager.getService("SurfaceFlinger");
            if (surfaceFlinger != null) {
                Slog.i(TAG_WM, "******* 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);
                data.recycle();
            }
        } catch (RemoteException ex) {
            Slog.e(TAG_WM, "Boot completed: SurfaceFlinger is dead!");
        }

        EventLog.writeEvent(EventLogTags.WM_BOOT_ANIMATION_DONE, SystemClock.uptimeMillis());
        Trace.asyncTraceEnd(TRACE_TAG_WINDOW_MANAGER, "Stop bootanim", 0);
        mDisplayEnabled = true;
        if (DEBUG_SCREEN_ON || DEBUG_BOOT) Slog.i(TAG_WM, "******************** ENABLING SCREEN!");

        // Enable input dispatch.
        mInputManagerCallback.setEventDispatchingLw(mEventDispatchingEnabled);
    }

    try {
        mActivityManager.bootAnimationComplete();
    } catch (RemoteException e) {
    }

    mPolicy.enableScreenAfterBoot();

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

SurfaceFlinger

// frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
void SurfaceFlinger::bootFinished()
{
    if (mStartPropertySetThread->join() != NO_ERROR) {
        ALOGE("Join StartPropertySetThread failed!");
    }
    const nsecs_t now = systemTime();
    const nsecs_t duration = now - mBootTime;
    ALOGI("Boot is finished (%ld ms)", long(ns2ms(duration)) );

    // wait patiently for the window manager death
    const String16 name("window");
    sp<IBinder> window(defaultServiceManager()->getService(name));
    if (window != 0) {
        window->linkToDeath(static_cast<IBinder::DeathRecipient*>(this));
    }
    sp<IBinder> input(defaultServiceManager()->getService(
            String16("inputflinger")));
    if (input == nullptr) {
        ALOGE("Failed to link to input service");
    } else {
        mInputFlinger = interface_cast<IInputFlinger>(input);
    }

    if (mVrFlinger) {
      mVrFlinger->OnBootFinished();
    }

    // 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");

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

    postMessageAsync(new LambdaMessage([this]() NO_THREAD_SAFETY_ANALYSIS {
        readPersistentProperties();
        mBootStage = BootStage::FINISHED;

        if (mRefreshRateConfigs->refreshRateSwitchingSupported()) {
            // set the refresh rate according to the policy
            const auto& performanceRefreshRate =
                    mRefreshRateConfigs->getRefreshRateFromType(RefreshRateType::PERFORMANCE);

            if (isDisplayConfigAllowed(performanceRefreshRate.configId)) {
                setRefreshRateTo(RefreshRateType::PERFORMANCE, Scheduler::ConfigEvent::None);
            } else {
                setRefreshRateTo(RefreshRateType::DEFAULT, Scheduler::ConfigEvent::None);
            }
        }
    }));
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值