AOSP15 SystemServer启动FallbackHome源码流程分析

83 阅读6分钟

在 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依靠WindowManagerTransition 系统(壳层切换)来消除视觉闪烁,因此 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) 的普通桌面应用。

  1. 平时Launcher3 优先级高,PMS 返回 Launcher3
  2. 未解锁Launcher3 被加密锁死,PMS 只能找到 FallbackHome
  3. 启动startHomeActivity 根本不管启动的是谁,它只负责执行 resolveHomeActivity 给它的结果。
  4. 结束FallbackHome中调用 maybeFinish再次获取ResolveInfo