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, ¶m) != 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);
}
}
}));
}

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

6541

被折叠的 条评论
为什么被折叠?



