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

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

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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) 
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值