在 AOSP 15 中,FallbackHome(位于 Settings 应用中)的设计目的是在真正的 Launcher 尚未准备好(如:处于 Direct Boot 模式、用户未解锁、或第三方 Launcher 崩溃)时,提供一个临时的 Home 容器。
以下是 SystemServer 从启动到最终拉起 FallbackHome 的详细源码流程分析
一、 启动入口:SystemServer 的就绪阶段
FallbackHome 的启动并非在 SystemServer 一开始就进行,而是发生在系统服务启动的后期阶段。
1. SystemServer.java: 启动核心服务(AMS, ATMS, PMS)。
2. 启动到指定阶段: 当系统执行到 PHASE_THIRD_PARTY_APPS_CAN_START (600) 时,AMS 的 systemReady() 会被调用。
// SystemServer.java
private void startOtherServices(@NonNull TimingsTraceAndSnailLogger timeline) {
// ...
// 调用ActivityManagerService的systemReady方法
mActivityManagerService.systemReady(() -> {
// 系统就绪后的回调逻辑
}, timeline);
}
二、核心调度:ATMS 尝试启动 Home
一旦 AMS 就绪,它会通知 ActivityTaskManagerService (ATMS) 去启动当前用户的 Home 活动。
1. ActivityManagerService.systemReady()
在 ActivityManagerService.java 中,它不再直接操作 Activity 栈,而是通过 LocalServices 调用 ATMS:
// ActivityManagerService.java (AOSP 15)
public void systemReady(final Runnable goingCallback, TimingsTraceAndSnailLogger timeline) {
// ... 前期初始化 ...
// 在 AMS 的回调逻辑中启动 Home
if (isBootingSystemUser && !UserManager.isHeadlessSystemUserMode()) {
// AOSP 15 使用这个方法代替了 AOSP 8 的 startHomeActivityLocked
mAtmInternal.startHomeOnAllDisplays(currentUserId, "systemReady");
}
}
2. ATMS 处理多显示器逻辑
startHomeOnAllDisplays 会进入 ActivityTaskManagerService 的内部,随后转交给 RootWindowContainer。这是因为 AOSP 15 需要支持折叠屏、外接屏等多个显示器同时拥有 Home。
// RootWindowContainer.java (AOSP 15)
boolean startHomeOnAllDisplays(int userId, String reason) {
boolean homeStarted = false;
// 遍历系统中所有的 RootTaskDisplayArea (对应不同屏幕)
for (int i = getChildCount() - 1; i >= 0; i--) {
final int displayId = getChildAt(i).mDisplayId;
// 在每个支持 Home 的屏幕上尝试启动
homeStarted |= startHomeOnDisplay(userId, reason, displayId);
}
return homeStarted;
}
3. RootWindowContainer.startHomeOnTaskDisplayArea()
startHomeOnAllDisplays->startHomeOnDisplay->startHomeOnTaskDisplayArea
boolean startHomeOnTaskDisplayArea(int userId, String reason, TaskDisplayArea taskDisplayArea,
boolean allowInstrumenting, boolean fromHomeKey) {
// ....
Intent homeIntent = null;
ActivityInfo aInfo = null;
if (taskDisplayArea == getDefaultTaskDisplayArea()
|| mWmService.shouldPlacePrimaryHomeOnDisplay(
taskDisplayArea.getDisplayId(), userId)) {
homeIntent = mService.getHomeIntent();
aInfo = resolveHomeActivity(userId, homeIntent);
} else if (shouldPlaceSecondaryHomeOnDisplayArea(taskDisplayArea)) {
Pair<ActivityInfo, Intent> info = resolveSecondaryHomeActivity(userId, taskDisplayArea);
aInfo = info.first;
homeIntent = info.second;
}
// 设置home的inten信息
homeIntent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name));
homeIntent.setFlags(homeIntent.getFlags() | FLAG_ACTIVITY_NEW_TASK);
if (fromHomeKey) {
homeIntent.putExtra(WindowManagerPolicy.EXTRA_FROM_HOME_KEY, true);
}
homeIntent.putExtra(WindowManagerPolicy.EXTRA_START_REASON, reason);
final String myReason = reason + ":" + userId + ":" + UserHandle.getUserId(
aInfo.applicationInfo.uid) + ":" + taskDisplayArea.getDisplayId();
mService.getActivityStartController().startHomeActivity(homeIntent, aInfo, myReason,
taskDisplayArea);
return true;
}
4. ActivityStartController.startHomeActivity()
void startHomeActivity(Intent intent, ActivityInfo aInfo, String reason,
TaskDisplayArea taskDisplayArea) {
final ActivityOptions options = ActivityOptions.makeBasic();
options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN);
if (!ActivityRecord.isResolverActivity(aInfo.name)) {
// 明确告诉系统,即将启动的任务属于 Home 层级。系统会根据这个标记将其放入 RootHomeTask 中
options.setLaunchActivityType(ACTIVITY_TYPE_HOME);
}
final int displayId = taskDisplayArea.getDisplayId();
options.setLaunchDisplayId(displayId);
options.setLaunchTaskDisplayArea(taskDisplayArea.mRemoteToken
.toWindowContainerToken());
// ...
mLastHomeActivityStartResult = obtainStarter(intent, "startHomeActivity: " + reason)
.setOutActivity(tmpOutRecord)
.setCallingUid(0) // 系统调用,UID 为 0
.setActivityInfo(aInfo) // 传入之前 resolve 出来的 ActivityInfo (如 FallbackHome)
.setActivityOptions(options.toBundle(),
Binder.getCallingPid(), Binder.getCallingUid())
.execute(); // 正式进入 Activity 启动管道
mLastHomeActivityStartRecord = tmpOutRecord[0];
if (rootHomeTask.mInResumeTopActivity) {
// 如果已经在 Resume 流程中了(防止递归执行),则发送一个调度请求
// 启动指令发送给 ActivityStarter 后,系统需要确保该 Activity 最终变为 Resumed 状态(可见并可交互)
mSupervisor.scheduleResumeTopActivities();
}
}
三、 选择FallbackHome启动逻辑
FallbackHome 的“回退”并不是靠一个简单的 if-else 硬编码实现的,而是靠 PackageManager 的过滤机制自动筛选出来的。
1.代码逻辑拆解
// 如果系统处于工厂测试模式,直接根据组件名获取 Activity 信息
if (comp != null) {
aInfo = AppGlobals.getPackageManager().getActivityInfo(comp, flags, userId);
} else {
// 【标准流程】
// 1. 获取 Intent 类型,筛选设置了<category android:name="android.intent.category.HOME" />
final String resolvedType = homeIntent.resolveTypeIfNeeded(mService.mContext.getContentResolver());
// 2. 调用 PMS 寻找匹配 CATEGORY_HOME 的最佳 Activity
final ResolveInfo info = mTaskSupervisor.resolveIntent(homeIntent, resolvedType,
userId, flags, Binder.getCallingUid(), Binder.getCallingPid());
if (info != null) {
aInfo = info.activityInfo; // 找到了,可能是 Launcher3,也可能是 FallbackHome
}
}
2. 为什么在此时会选中 FallbackHome?
关键在于代码里的 flags 和 userId。
在系统刚刚启动(Direct Boot 阶段)时,磁盘是加密的。PMS 在执行 resolveIntent 时会遵循以下规则:
1)扫描所有 Home:
PMS 找到系统内所有声明了 的 Activity。
<!-- FallbackHome -->
<application
android:directBootAware="true">
<activity android:name=".FallbackHome">
<intent-filter android:priority="-1000">
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.HOME" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
</application>
<!-- Launch3 -->
<application>
<activity
android:name="com.android.launcher3.Launcher">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<action android:name="android.intent.action.SHOW_WORK_APPS" />
<category android:name="android.intent.category.HOME" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.MONKEY"/>
<category android:name="android.intent.category.LAUNCHER_APP" />
</intent-filter>
</activity>
</application>
2)检查加密感知 (Direct Boot Aware):
- Launcher3:通常没有 android:directBootAware="true",所以此时被 PMS 排除掉。
- FallbackHome:在 Settings 应用中声明了 android:directBootAware="true",且位于设备保护(DE)存储区。
3)优先级筛选:
-
如果只剩下
FallbackHome一个人符合条件,PMS 就会返回它的ResolveInfo。 -
如果你代码里的
info不为 null,那么 aInfo 拿到的就是FallbackHome。
3. 如果 info == null 怎么办?
如果在这一步 info 依然是 null(即连 FallbackHome 都找不到,或者 PMS 还没启动好),那么 aInfo 就会是 null。
回到调用它的地方(startHomeOnTaskDisplayAreay):
boolean startHomeOnTaskDisplayArea(int userId, String reason, TaskDisplayArea taskDisplayArea,
boolean allowInstrumenting, boolean fromHomeKey) {
// ...
Intent homeIntent = null;
ActivityInfo aInfo = null;
if (taskDisplayArea == getDefaultTaskDisplayArea()
|| mWmService.shouldPlacePrimaryHomeOnDisplay(
taskDisplayArea.getDisplayId(), userId)) {
homeIntent = mService.getHomeIntent();
aInfo = resolveHomeActivity(userId, homeIntent);
} else if (shouldPlaceSecondaryHomeOnDisplayArea(taskDisplayArea)) {
Pair<ActivityInfo, Intent> info = resolveSecondaryHomeActivity(userId, taskDisplayArea);
aInfo = info.first;
homeIntent = info.second;
}
// 如果连 FallbackHome 都没有,系统可能会卡在黑屏,或者等待下一次恢复尝试
if (aInfo == null || homeIntent == null) {
return false;
}
// ...
mService.getActivityStartController().startHomeActivity(homeIntent, aInfo, myReason,
taskDisplayArea);
}
四、 FallbackHome结束启动Launcher
当 FallbackHome 启动后,它通过 Settings 应用内部的逻辑完成从“保底桌面”到“正式桌面”的切换
1. FallbackHome 的核心:监听解锁广播
源码:packages/apps/Settings/src/com/android/settings/FallbackHome.java
在 onCreate 阶段,它会注册一个监听 ACTION_USER_UNLOCKED(用户已解锁)的广播接收器
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// ....
// 过度动画处理
mWallManager = getSystemService(WallpaperManager.class);
if (mWallManager == null) {
Log.w(TAG, "Wallpaper manager isn't ready, can't listen to color changes!");
} else {
loadWallpaperColors(flags);
}
getWindow().getDecorView().setSystemUiVisibility(flags);
// 注册ACTION_USER_UNLOCKED
registerReceiver(mReceiver, new IntentFilter(Intent.ACTION_USER_UNLOCKED));
maybeFinish();
}
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// 核心方法
maybeFinish();
}
};
2. 核心切换逻辑:maybeFinish()
maybeFinish() 是 FallbackHome 的“灵魂”方法。它负责判断:现在是不是该让位给真正的 Launcher 了?
private void maybeFinish() {
// 1. 确认用户是否已经解锁(CE 存储是否可用)
if (getSystemService(UserManager.class).isUserUnlocked()) {
final Intent homeIntent = new Intent(Intent.ACTION_MAIN)
.addCategory(Intent.CATEGORY_HOME);
// 2. 关键:重新解析当前的 Home Activity
// 此时由于磁盘已解锁,PackageManager 能够找到真正的 Launcher (如 Pixel Launcher)
final ResolveInfo homeInfo = getPackageManager().resolveActivity(homeIntent, 0);
if (Objects.equals(getPackageName(), homeInfo.activityInfo.packageName)) {
// 循环调用maybeFinish方法
mHandler.sendEmptyMessageDelayed(0, 500);
} else {
// 3. 如果解析出来的 Home 不是 FallbackHome 所在的 Settings 包
// 说明找到了真正的 Launcher,FallbackHome 功成身退
getSystemService(PowerManager.class).userActivity(
SystemClock.uptimeMillis(), false);
finish();
}
} else {
// 用户没有解锁
}
}
3. 触发第二次 Home 启动流程
一旦 FallbackHome 调用了 finish(),系统会发生以下连锁反应:
- ActivityStack 管理:ActivityTaskManagerService 检测到当前 Home 栈顶的 Activity (FallbackHome) 正在销毁。
- 触发恢复逻辑:由于 Home 栈变空或状态改变,系统会自动调用我们在前几轮讨论的 RootWindowContainer.resumeHomeActivity()(在 AOSP 15 中可能是通过 TaskDisplayArea 触发的下一次调度)。
- 重新解析 (resolveHomeActivity):
- 这一次,PackageManager 运行在已解锁状态下。
- 它会发现高优先级(通常 priority=0 或更高)的第三方 Launcher 已经可以访问了。
- 而 FallbackHome 的优先级是 -1000。
- 拉起真正 Launcher:系统调用 startHomeActivity,真正的桌面正式出现在用户面前。
4. AOSP 15 的细节优化:延迟动画
在 AOSP 15 中,为了避免切换时的闪烁,FallbackHome 还会与 WallpaperManager 配合:
- 渐变动画:
FallbackHome会在maybeFinish时启动一个淡出动画。 - 壁纸接管:它会确保真正的 Launcher 在拉起时,壁纸能够平滑过渡,而不是突然从黑屏跳到桌面,在AOSP 15依靠
WindowManager的Transition系统(壳层切换)来消除视觉闪烁,因此FallbackHome逻辑变得极简,旧版本是在finish中加mHandler.postDelayed(() -> finish(), 500);。
五、流程图
SystemServer.java
└── startOtherServices()
└── mActivityManagerService.systemReady(goingCallback)
└── [Runnable] (匿名内部类/Lambda)
└── ActivityTaskManagerInternal.startHomeOnAllDisplays(userId, "systemReady")
ActivityTaskManagerService.java
└── startHomeOnAllDisplays()
└── RootWindowContainer.startHomeOnDisplay()
└── RootWindowContainer.startHomeOnTaskDisplayArea()
├── RootWindowContainer.resolveHomeActivity() <-- 【关键:解析逻辑】
│ └── PackageManagerService.queryIntentActivities()
│ └── (由于处于 Direct Boot 模式,仅返回标记了 directBootAware="true" 的 FallbackHome)
└── ActivityStartController.startHomeActivity(homeIntent, aInfo, ...)
ActivityStartController.java
└── startHomeActivity()
└── ActivityStarter.execute()
└── ... (经过一系列 Lifecycle 调用)
└── FallbackHome.onCreate() <-- 【FallbackHome 界面出现】
└── FallbackHome.registerReceiver(mReceiver, ACTION_USER_UNLOCKED)
五、 总结:FallbackHome 的本质
FallbackHome 并不是在代码里写死的备胎,它是一个 优先级极低但兼容性极强(支持 Direct Boot) 的普通桌面应用。
- 平时:
Launcher3优先级高,PMS 返回Launcher3。 - 未解锁:
Launcher3被加密锁死,PMS 只能找到FallbackHome。 - 启动:
startHomeActivity根本不管启动的是谁,它只负责执行resolveHomeActivity给它的结果。 - 结束:
FallbackHome中调用maybeFinish再次获取ResolveInfo。