参考博客
接着上面Android开机动画的显示(一)分析,开机动画怎么结束的,又是如何显示桌面(锁屏界面)的。
一、代码流程
在前面的 Android开机流程 可以知道,SystemServer最后会调用到AMS.systemReady
1.1 AMS.systemReady
......
Slog.i(TAG, "System now ready");
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_AMS_READY,
SystemClock.uptimeMillis());
synchronized(this) {
// mFactoryTest 就是属性 ro.factorytest ,这个值默认为0,代表非工厂模式
if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL) {
// 正常不进入.......
}
......
// 标记正在启动,启动完成后置回false
mBooting = true;
// isSplitSystemUser()最后获取的还是属性ro.fw.system_user_split,默认值是false,目前没有配置这个值
// USER_SETUP_COMPLETE 这个标记了是否设置过了开机向导
if (UserManager.isSplitSystemUser() &&
Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.USER_SETUP_COMPLETE, 0) != 0) {
ComponentName cName = new ComponentName(mContext, SystemUserHomeActivity.class);
try {
AppGlobals.getPackageManager().setComponentEnabledSetting(cName,
PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0,
UserHandle.USER_SYSTEM);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
}
// 重点在这里,启动桌面也就是launcher
startHomeActivityLocked(currentUserId, "systemReady");
......
1.2 AMS.startHomeActivityLocked
boolean startHomeActivityLocked(int userId, String reason) {
if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL
&& mTopAction == null) {
// 如上分析,这里是进入了工厂测试模式,但是找不到工模应用
// 直接返回,显示错误信息
return false;
}
// 1.2.1 获取待启动的桌面Activity的Intent
Intent intent = getHomeIntent();
// 解析桌面应用,在这里面识别默认桌面应用,这里不深究,后续分析桌面应用的启动
ActivityInfo aInfo = resolveActivityInfo(intent, STOCK_PM_FLAGS, userId);
if (aInfo != null) {
intent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name));
aInfo = new ActivityInfo(aInfo);
aInfo.applicationInfo = getAppInfoForUser(aInfo.applicationInfo, userId);
ProcessRecord app = getProcessRecordLocked(aInfo.processName,
aInfo.applicationInfo.uid, true);
if (app == null || app.instr == null) {
intent.setFlags(intent.getFlags() | FLAG_ACTIVITY_NEW_TASK);
final int resolvedUserId = UserHandle.getUserId(aInfo.applicationInfo.uid);
final String myReason = reason + ":" + userId + ":" + resolvedUserId;
// 1.3 启动桌面应用的Activity!
mActivityStartController.startHomeActivity(intent, aInfo, myReason);
}
} else {
Slog.wtf(TAG, "No home screen found for " + intent, new Throwable());
}
return true;
}
1.2.1 AMS.getHomeIntent
获取HomeActivity的启动Intent,我们知道只要带有Intent.ACTION_MAIN以及Intent.CATEGORY_HOME,就会被识别成桌面应用。
// 正常模式下mTopComponent和mTopData都是null的
ComponentName mTopComponent;
String mTopAction = Intent.ACTION_MAIN;
String mTopData;
Intent getHomeIntent() {
Intent intent = new Intent(mTopAction, mTopData != null ? Uri.parse(mTopData) : null);
intent.setComponent(mTopComponent);
intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
intent.addCategory(Intent.CATEGORY_HOME);
}
return intent;
}
1.3 Activity启动准备完毕
一个Activity组件在启动起来之后,就会被记录起来,等到它所运行在的主线程空闲的时候,这个主线程就会向ActivityManagerService发送一个Activity组件空闲的通知。由于应用程序Launcher是系统中第一个被启动的应用程序,即它的根Activity组件是系统中第一个被启动的Activity组件,因此,当ActivityManagerService接收到它的空闲通知的时候,就可以知道系统是刚刚启动起来的。在这种情况下,ActivityManagerService就会停止显示开机动画,以便可以在屏幕中显示应用程序Lancher的界面。
应用程序的主线程是通过ActivityThread类来描述的,它实现在文件frameworks/base/core/java/android/app/ActivityThread.java中。每当有一个新的Activity组件启动起来的时候,ActivityThread类都会向它所描述的应用程序主线程的消息队列注册一个类型为Idler的空闲消息处理器(详见Android消息机制的分析)。这样一个应用程序的主线程就可以在空闲的时候,向ActivityManagerService发送一个Activity组件空闲通知,相当于是通知ActivityManagerService,一个新的Activity组件已经准备就绪了。
应用程序主线程通知AMS有Activity组件空闲通知后,AMS就会调用mStackSupervisor.activityIdleInternalLocked,在这里面检查booting状态并决定是否向AMS报告结束启动过程,也就是结束显示开机动画。
1.3.1 ActivityStack.activityIdleInternal
final ActivityRecord activityIdleInternalLocked(final IBinder token, boolean fromTimeout,
boolean processPausingActivities, Configuration config) {
if (DEBUG_ALL) Slog.v(TAG, "Activity idle: " + token);
......
if (isFocusedStack(r.getStack()) || fromTimeout) {
// 检查当前booting状态
booting = checkFinishBootingLocked();
}
......
}
private boolean checkFinishBootingLocked() {
// mService就是AMS
final boolean booting = mService.mBooting;
boolean enableScreen = false;
mService.mBooting = false;
if (!mService.mBooted) {
mService.mBooted = true;
enableScreen = true;
}
if (booting || enableScreen) {
// 1.3.2 当系统正处于启动状态,且mBooted(已经启动过了)为false
// AMS调用postFinishBooting,结束开机动画的显示
// booting和enableScreen均为true
mService.postFinishBooting(booting, enableScreen);
}
return booting;
}
1.3.2 AMS.postFinishBooting
void postFinishBooting(boolean finishBooting, boolean enableScreen) {
mHandler.sendMessage(mHandler.obtainMessage(FINISH_BOOTING_MSG,
finishBooting ? 1 : 0, enableScreen ? 1 : 0));
}
final class MainHandler extends Handler {
......
public void handleMessage(Message msg) {
case FINISH_BOOTING_MSG: {
if (msg.arg1 != 0) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "FinishBooting");
finishBooting();
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
if (msg.arg2 != 0) {
// 1.3.3 结束开机动画
enableScreenAfterBoot();
}
break;
......
}
1.3.3 AMS.enableScreenAfterBoot
void enableScreenAfterBoot() {
// 在event.log里面打印出 boot_progress_enable_screen
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_ENABLE_SCREEN,
SystemClock.uptimeMillis());
// 1.3.4 通知到WMS可以停止显示开机动画了
mWindowManager.enableScreenAfterBoot();
synchronized (this) {
updateEventDispatchingLocked();
}
}
1.3.4 WMS.enableScreenAfterBoot
public void enableScreenAfterBoot() {
synchronized(mWindowMap) {
if (mSystemBooted) {
return;
}
// 用来记录系统是否已经启动完成,true代表完成
mSystemBooted = true;
hideBootMessagesLocked();
// 设置30s超时
mH.sendEmptyMessageDelayed(H.BOOT_TIMEOUT, 30 * 1000);
}
mPolicy.systemBooted();
// 1.3.5 执行停止显示开机动画的操作
performEnableScreen();
}
1.3.5 WMS.performEnableScreen
private void performEnableScreen() {
synchronized(mWindowMap) {
if (mDisplayEnabled) {
return;
}
......
if (!mBootAnimationStopped) {
Trace.asyncTraceBegin(TRACE_TAG_WINDOW_MANAGER, "Stop bootanim", 0);
// 设置属性以通知bootanim进程退出
SystemProperties.set("service.bootanim.exit", "1");
mBootAnimationStopped = true;
}
// 等待确认开机动画确实退出了
if (!mForceDisplayEnabled && !checkBootAnimationCompleteLocked()) {
if (DEBUG_BOOT) Slog.i(TAG_WM, "performEnableScreen: Waiting for anim complete");
return;
}
// 执行SurfaceFlinger结束开机动画的操作
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.
mInputMonitor.setEventDispatchingLw(mEventDispatchingEnabled);
}
try {
mActivityManager.bootAnimationComplete();
} catch (RemoteException e) {
}
mPolicy.enableScreenAfterBoot();
// Make sure the last requested orientation has been applied.
updateRotationUnchecked(false, false);
}
在这个步骤里就会设置属性service.bootanim.exit为1以通知bootanim可以结束开机动画的播放了。还记得之前的BootAnimation.cpp:playAnimation(const Animation&)方法,这个方法在每帧渲染结束后都会区检查属性service.bootanim.exit的值以决定是否退出开机动画。
1.4. BootAnimation.cpp:checkEixt()
由Android开机动画的显示(一)可以知道,播放开机动画过程中每帧绘制结束后都会调用这个方法。
static const char EXIT_PROP_NAME[] = "service.bootanim.exit";
void BootAnimation::checkExit() {
char value[PROPERTY_VALUE_MAX];
property_get(EXIT_PROP_NAME, value, "0");
int exitnow = atoi(value);
if (exitnow) {
// 属性值不为0,代表可以AMS层启动完毕,launcher界面也准备好了
// Thread请求退出
requestExit();
// 这里的callback其实是AudioAnimationCallbacks
mCallbacks->shutdown();
}
}
二、总结
至此,Android系统的三个开机画面的显示过程就分析完成了。通过这个三个开机画面的显示过程分析,我们学习到:
-
在内核层,系统屏幕是使用一个称为帧缓冲区的硬件设备来描述的,而用户空间的应用程序可以通过设备文件/dev/fb0或者/dev/graphics/fb0来操作这个硬件设备。实际上,帧缓冲区本身并不是一个真正的硬件,它只不过是对显卡的一个抽象表示,不过,我们通过访帧缓冲区就可以间接地操作显卡内存以及显卡中的其它寄存器。
-
OpenGL是通过EGL接口来渲染屏幕,而EGL接口是通过ANativeWindow类来间接地渲染屏幕的。我们可以将ANativeWindow类理解成一个Android系统的本地窗口类,即相当于是Windows系统中的窗口句柄概念,它最终是通过文件/dev/fb0或者/dev/graphics/fb0来渲染屏幕的。
-
每当我们设置一个系统属性的时候,init进程都会接收到一个系统属性变化事件。当发生变化的系统属性的名称等于“ctl.start”或者“ctl.stop”,那么实际上是向init进程发出一个启动或者停止服务的命令。
-
BootAnimation通过在每帧绘制完毕后检查属性service.bootanim.exit的值判断是否结束播放开机动画,而这个值会在launcher应用的根Activity启动完成处于idle状态时由该应用通知AMS进而通知WMS设置的。其实在这一过程中SurfaceFlinger也会设置一次。
开机动画的流程是分析完毕了,但还有个疑问,launcher界面是什么时候、怎么显示到屏幕上的?这就涉及到SurfaceFlinger、WMS与应用进程的交互了。后面先分析SurfaceFlinger的工作原理,一步步剖析Andorid的整个UI架构。