安卓15开机动画BootAnimation启动源码简单分析

前言

安卓 启动链路里,SurfaceFlinger 一定先于 BootAnimation 工作起来,否则开机动画根本没有显示目标。因此,SurfaceFlinger 是 bootanimation 的前置依赖。

相关源码路径

bash 复制代码
bootanimation   frameworks/base/cmds/bootanimation/
surfaceflinger  frameworks/native/services/surfaceflinger/
init            system/core/init/

surfaceflinger启动

安卓内核起来后会启动第一个init进程。init进程会根据init.rc(system/core/rootdir/init.rc)配置启动surfaceflinger进程。

rc 复制代码
# 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

surfaceflinger有一个单独的rc文件,定义了class core animation

rc 复制代码
# system/core/rootdir/init.rc

on boot
	......
    chown system system /sys/kernel/ipv4/tcp_rmem_max
    chown root radio /proc/cmdline
    chown root system /proc/bootconfig

    # Define default initial receive window size in segments.
    setprop net.tcp_def_init_rwnd 60

    # Start standard binderized HAL daemons
    class_start hal

	# 这里,启动所有core类
    class_start core

init.rc 的执行流程中,Android 不再倾向于逐个手动 start 服务,而是通过 class_start 命令来批量启动同一类别的服务。

  • 当系统启动进入特定阶段(如 on boot)时,init.rc 会执行 class_start core
  • 因为 SurfaceFlinger 被标记为 core 类,init 进程会自动扫描所有加载的 .rc 文件,并启动所有属于该类的服务。

surfaceflinger进程便启动了,跟着就会跑进程的main()函数,即frameworks/native/services/surfaceflinger/main_surfaceflinger.cpp。这个main_surfaceflinger.cpp就是第一个执行的文件,因为它包含了程序的入口点 main() 函数。

cpp 复制代码
/* main_surfaceflinger.cpp */

int main(int, char**) {
    sp<SurfaceFlinger> flinger = surfaceflinger::createSurfaceFlinger(); // 创建surfaceflinger服务
    
    flinger->init(); // 初始化SurfaceFlinger
    
    // 将SurfaceFlinger注册为系统服务
    sp<IServiceManager> sm(defaultServiceManager());
    sm->addService(String16(SurfaceFlinger::getServiceName()), flinger, false,
                   IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL | IServiceManager::DUMP_FLAG_PROTO);
    
    // run surface flinger in this thread
    flinger->run();

    return 0;
}

cpp 复制代码
/* SurfaceFlinger.cpp */
void SurfaceFlinger::init() FTL_FAKE_GUARD(kMainThreadContext) {
	...
    // 异步设置系统属性
    // Avoid blocking the main thread on `init` to set properties.
    mInitBootPropsFuture.callOnce([this] {
        return std::async(std::launch::async, &SurfaceFlinger::initBootProperties, this);
    });
    ...
}

void SurfaceFlinger::initBootProperties() {
    property_set("service.sf.present_timestamp", mHasReliablePresentFences ? "1" : "0");

    if (base::GetBoolProperty("debug.sf.boot_animation"s, true)) {
        // Reset and (if needed) start BootAnimation.
        property_set("service.bootanim.exit", "0"); // 这个属性bootanimation进程里会周期检查,=1时就退出动画,这里=0表示要播放动画
        property_set("service.bootanim.progress", "0");
        property_set("ctl.start", "bootanim");	// 启动 开机动画
    }
}

这样bootanim进程就会启动?凭什么设置了一个属性就启动了?


那么下面我们来看,/system/core/init/main.cpp ,在看init进程的main.cpp的main函数中:

cpp 复制代码
/* system/core/init/main.cpp */

int main(int argc, char** argv) {
...

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

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

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

        if (!strcmp(argv[1], "second_stage")) {
            return SecondStageMain(argc, argv); // 第二阶段则进一步加载系统配置并启动系统服务,SecondStageMain是在init.cpp中
        }
    }

    return FirstStageMain(argc, argv);
}
cpp 复制代码
/* system/core/init/init.cpp */

int SecondStageMain(int argc, char** argv) {
	...
    StartPropertyService(&property_fd); // 在property_service.cpp中定义,这里先执行
    ...

	while (true) {
        ...
        auto epoll_result = epoll.Wait(epoll_timeout); // 等待事件(被 WakeMainInitThread() 唤醒),这里等到后面才会用到
        if (!epoll_result.ok()) {
            LOG(ERROR) << epoll_result.error();
        }
        if (!IsShuttingDown()) {
            HandleControlMessages();
            SetUsbController();
        }
    }

    return 0;        
}
cpp 复制代码
/* system/core/init/property_service.cpp */

void StartPropertyService(int* epoll_socket) {
    InitPropertySet("ro.property_service.version", "2");

    int sockets[2];
    if (socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sockets) != 0) {
        PLOG(FATAL) << "Failed to socketpair() between property_service and init";
    }
    *epoll_socket = from_init_socket = sockets[0];
    init_socket = sockets[1];
    StartSendingMessages();

    // 启动属性服务线程
    StartThread(PROP_SERVICE_FOR_SYSTEM_NAME, 0660, AID_SYSTEM, property_service_for_system_thread,
                true);
    StartThread(PROP_SERVICE_NAME, 0666, 0, property_service_thread, false);

    auto async_persist_writes =
            android::base::GetBoolProperty("ro.property_service.async_persist_writes", false);

    if (async_persist_writes) {
        persist_write_thread = std::make_unique<PersistWriteThread>();
    }
}




void StartThread(const char* name, int mode, int gid, std::thread& t, bool listen_init) {
    int fd = -1;
    if (auto result = CreateSocket(name, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
                                   /*passcred=*/false, /*should_listen=*/false, mode, /*uid=*/0,
                                   /*gid=*/gid, /*socketcon=*/{});
        result.ok()) {
        fd = *result;
    } else {
        LOG(FATAL) << "start_property_service socket creation failed: " << result.error();
    }

    listen(fd, 8);

    // 创建新线程,运行 PropertyServiceThread 函数
    auto new_thread = std::thread(PropertyServiceThread, fd, listen_init);
    t.swap(new_thread);
}




static void PropertyServiceThread(int fd, bool listen_init) {
    Epoll epoll; // 创建一个 epoll 实例,监听多个文件描述符(FD)的事件
    if (auto result = epoll.Open(); !result.ok()) {
        LOG(FATAL) << result.error();
    }

    // 将属性服务的 socket(fd)注册到 epoll,当有数据可读时,调用 handle_property_set_fd 处理。
    if (auto result = epoll.RegisterHandler(fd, std::bind(handle_property_set_fd, fd));
        !result.ok()) {
        LOG(FATAL) << result.error();
    }

    ...
}





static void handle_property_set_fd(int fd) {
switch (cmd) {
    case PROP_MSG_SETPROP: {
        ...
        const auto& cr = socket.cred();
        std::string error;
        // 这里的HandlePropertySetNoSocket,最后也会调用HandlePropertySet
        auto result = HandlePropertySetNoSocket(prop_name, prop_value, source_context, cr, &error);
        if (result != PROP_SUCCESS) {
            LOG(ERROR) << "Unable to set property '" << prop_name << "' from uid:" << cr.uid
                       << " gid:" << cr.gid << " pid:" << cr.pid << ": " << error;
        }

        break;
      }

    case PROP_MSG_SETPROP2: {
        ...
        // 这里的HandlePropertySet
        auto result = HandlePropertySet(name, value, source_context, cr, &socket, &error);
        if (!result) {
            // Result will be sent after completion.
            return;
        }
        if (*result != PROP_SUCCESS) {
            LOG(ERROR) << "Unable to set property '" << name << "' 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;
    }
}

在之前的SurfaceFlinger::initBootProperties中,传入的参数name=ctl.start,value=bootanim

cpp 复制代码
/* system/core/init/property_service.cpp */

std::optional<uint32_t> HandlePropertySet(const std::string& name, const std::string& value,
                                          const std::string& source_context, const ucred& cr,
                                          SocketConnection* socket, std::string* error) {
    if (auto ret = CheckPermissions(name, value, source_context, cr, error); ret != PROP_SUCCESS) {
        return {ret};
    }

    if (StartsWith(name, "ctl.")) {
        // 这里,最终经过裁剪后,传入的是start和bootanim
        return {SendControlMessage(name.c_str() + 4, value, cr.pid, socket, error)};
    }

    ...
}

static uint32_t SendControlMessage(const std::string& msg, const std::string& name, pid_t pid,
                                   SocketConnection* socket, std::string* error) {
    ...

    bool queue_success = QueueControlMessage(msg, name, pid, fd); // 将消息加入队列
    if (!queue_success && fd != -1) {
        uint32_t response = PROP_ERROR_HANDLE_CONTROL_MESSAGE;
        TEMP_FAILURE_RETRY(send(fd, &response, sizeof(response), 0));
        close(fd);
    }

    return PROP_SUCCESS;
}
cpp 复制代码
/* system/core/init/init.cpp */

bool QueueControlMessage(const std::string& message, const std::string& name, pid_t pid, int fd) {
    auto lock = std::lock_guard{pending_control_messages_lock};
    if (pending_control_messages.size() > 100) {
        LOG(ERROR) << "Too many pending control messages, dropped '" << message << "' for '" << name
                   << "' from pid: " << pid;
        return false;
    }
    pending_control_messages.push({message, name, pid, fd});
    WakeMainInitThread(); // 唤醒主线程!通知 init 主循环处理
    return true;
}

在回到system/core/init/init.cpp的SecondStageMain主循环处理:

cpp 复制代码
/* system/core/init/init.cpp */

int SecondStageMain(int argc, char** argv) {

	while (true) {
        ...
        auto epoll_result = epoll.Wait(epoll_timeout); // 等待事件(被 WakeMainInitThread() 唤醒)
        if (!epoll_result.ok()) {
            LOG(ERROR) << epoll_result.error();
        }
        if (!IsShuttingDown()) {
            HandleControlMessages(); //这里,HandleControlMessages会调用HandleControlMessage
            SetUsbController();
        }
    }

    return 0;        
}



static bool HandleControlMessage(std::string_view message, const std::string& name,
                                 pid_t from_pid) {    
	const auto& map = GetControlMessageMap(); //这里,查表获取处理函数,GetControlMessageMap定义在下面
    const auto it = map.find(action); // // action = "start"
    if (it == map.end()) {
        LOG(ERROR) << "Unknown control msg '" << message << "'";
        return false;
    }
    const auto& function = it->second; // 对应启动服务的函数指针

    // // 调用实际的 Start 函数
    if (auto result = function(service); !result.ok()) {
        LOG(ERROR) << "Control message: Could not ctl." << message << " for '" << name
                   << "' from pid: " << from_pid << " (" << process_cmdline
                   << "): " << result.error();
        return false;
    }
}    

// 查表对应的处理函数
static const std::map<std::string, ControlMessageFunction, std::less<>>& GetControlMessageMap() {
    // clang-format off
    static const std::map<std::string, ControlMessageFunction, std::less<>> control_message_functions = {
		...
        {"start",             DoControlStart}, // 我们是这个 start
        {"stop",              DoControlStop},
        {"restart",           DoControlRestart},
    };
    // clang-format on
    return control_message_functions;
}
static Result<void> DoControlStart(Service* service) {
    return service->Start(); //至此,就启动了bootanimation
}

其它

"挂载分区"含义

给物理存储分区分配一个 "可访问的目录路径";

安卓场景的具体例子

比如系统启动时 init.rc 里的这句配置:

bash 复制代码
mount ext4 /dev/block/system /system ro wait

拆解就是:

  • ext4:分区的文件系统格式;
  • /dev/block/system:物理存储分区的设备节点;
  • /system:挂载点;
  • ro:挂载为只读。

挂载前:/system 只是一个空目录,/dev/block/system 是一块无法访问的存储;

挂载后:访问 /system/bin/bootanimation 就等于直接读写 /dev/block/system 分区里的 bootanimation 文件。

相关推荐
Ehtan_Zheng10 小时前
ActivityMetricsLogger 深度剖析:系统如何追踪启动耗时
android
用户693717500138411 小时前
Android 开发,别只钻技术一亩三分地,也该学点“广度”了
android·前端·后端
唔6611 小时前
原生 Android(Kotlin)仅串口「继承架构」完整案例二
android·开发语言·kotlin
一直都在57211 小时前
MySQL索引优化
android·数据库·mysql
代码s贝多芬的音符12 小时前
android mlkit 实现仰卧起坐和俯卧撑识别
android
jwn99913 小时前
Laravel9.x核心特性全解析
android
今天又在写代码14 小时前
数据智能分析平台部署服务器
android·服务器·adb
梦里花开知多少15 小时前
深入谈谈Launcher的启动流程
android·架构
jwn99915 小时前
Laravel11.x新特性全解析
android·开发语言·php·laravel
我就是马云飞15 小时前
停更5年后,我为什么重新开始写技术内容了
android·前端·程序员