一、Android 系统启动流程
-
Bootloader 系统引导
-
启动 Linux 内核
-
启动 init 进程
-
启动 Zygote 进程
-
启动 SystemServer 进程
- 启动 Binder 线程池
- 创建 SystemServiceManager 并启动各种 SystemService
二、启动设置向导或 Launcher
SystemServer 会在 startBootstrapServices() 方法中会启动 ActivityManagerService 。
private void startBootstrapServices() {
...
// Activity manager runs the show.
mActivityManagerService = mSystemServiceManager.startService(
ActivityManagerService.Lifecycle.class).getService();
...
}
复制代码
在 startOtherServices() 方法中会调用 ActivityManagerService 的 systemReday() 方法。
private void startOtherServices() {
...
mActivityManagerService.systemReady(new Runnable() {
@Override
public void run() {
Slog.i(TAG, "Making services ready");
mSystemServiceManager.startBootPhase(
SystemService.PHASE_ACTIVITY_MANAGER_READY);
...
}
...
}
复制代码
ActivityManagerService 的 systemReday() 方法中会调用 startHomeActivityLocked() 方法。
public void systemReady(final Runnable goingCallback) {
...
synchronized (this) {
...
startHomeActivityLocked(currentUserId, "systemReady");
...
}
...
}
复制代码
startHomeActivityLocked() 方法中会获取到 Action 为 Intent.ACTION_MAIN,Category 为 Intent.CATEGORY_HOME 的 Intent,根据该 Intent 获取到符合条件的应用,并判断该应用是否已经启动,没有启动则启动该应用。
...
String mTopAction = Intent.ACTION_MAIN;
...
// 获取 Action 为 Intent.ACTION_MAIN,Category 为 Intent.CATEGORY_HOME 的 Intent
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;
}
boolean startHomeActivityLocked(int userId, String reason) {
if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL
&& mTopAction == null) {
// We are running in factory test mode, but unable to find
// the factory test app, so just sit around displaying the
// error message and don't try to start anything.
return false;
}
Intent intent = getHomeIntent();
// 获取符合条件的应用
ActivityInfo aInfo = resolveActivityInfo(intent, STOCK_PM_FLAGS, userId);
if (aInfo != null) {
intent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name));
// Don't do this if the home app is currently being
// instrumented.
aInfo = new ActivityInfo(aInfo);
aInfo.applicationInfo = getAppInfoForUser(aInfo.applicationInfo, userId);
ProcessRecord app = getProcessRecordLocked(aInfo.processName,
aInfo.applicationInfo.uid, true);
// 判断应用是否启动,未启动则启动该应用程序
if (app == null || app.instrumentationClass == null) {
intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
// 启动应用程序
mActivityStarter.startHomeActivityLocked(intent, aInfo, reason);
}
} else {
Slog.wtf(TAG, "No home screen found for " + intent, new Throwable());
}
return true;
}
复制代码
一般情况下,被启动的应用就是 Launcher,因为 Launcher 的 Manifest 文件中有匹配了 Action 为 Intent.ACTION_MAIN,Category 为 Intent.CATEGORY_HOME 的 Activity。
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.launcher3">
<uses-sdk android:targetSdkVersion="23" android:minSdkVersion="21"/>
...
<application
android:backupAgent="com.android.launcher3.LauncherBackupAgent"
android:fullBackupOnly="true"
android:fullBackupContent="@xml/backupscheme"
android:hardwareAccelerated="true"
android:icon="@mipmap/ic_launcher_home"
android:label="@string/derived_app_name"
android:largeHeap="@bool/config_largeHeap"
android:restoreAnyVersion="true"
android:supportsRtl="true" >
<activity
android:name="com.android.launcher3.Launcher"
android:launchMode="singleTask"
android:clearTaskOnLaunch="true"
android:stateNotNeeded="true"
android:theme="@style/LauncherTheme"
android:windowSoftInputMode="adjustPan"
android:screenOrientation="nosensor"
android:configChanges="keyboard|keyboardHidden|navigation"
android:resumeWhilePausing="true"
android:taskAffinity=""
android:enabled="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.HOME" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.MONKEY"/>
intent-filter>
activity>
...
application>
manifest>
复制代码
但是,当首次开机时,被启动的应用就是设置向导,因为设置向导的 Manifest 文件中也有匹配了 Action 为 Intent.ACTION_MAIN,Category 为 Intent.CATEGORY_HOME 的 Activity,并且优先级高于 Launcher。
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.provision">
...
<application>
<activity
android:name="DefaultActivity"
android:theme="@android:style/Theme.Translucent.NoTitleBar.Fullscreen"
android:excludeFromRecents="true">
<intent-filter android:priority="1">
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.HOME" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.SETUP_WIZARD" />
intent-filter>
activity>
application>
manifest>
复制代码
Launcher 的 Manifest 中 intent-filter 没有设置优先级,默认为 0;设置向导的 Manifest 中 intent-filter 的优先级为 1;所以在 resolveActivityInfo() 方法获取符合的应用时会优先获取到设置向导。
private ActivityInfo resolveActivityInfo(Intent intent, int flags, int userId) {
ActivityInfo ai = null;
ComponentName comp = intent.getComponent();
try {
if (comp != null) {
// Factory test.
ai = AppGlobals.getPackageManager().getActivityInfo(comp, flags, userId);
} else {
ResolveInfo info = AppGlobals.getPackageManager().resolveIntent(
intent,
intent.resolveTypeIfNeeded(mContext.getContentResolver()),
flags, userId);
if (info != null) {
ai = info.activityInfo;
}
}
} catch (RemoteException e) {
// ignore
}
return ai;
}
复制代码
获取最优 Activity 的具体实现在 PackageManagerService 的 chooseBestActivity() 方法中。
三、priority 及 android.intent.category.SETUP_WIZARD
Manifest 中 Activity 的 intent-filter 的优先级设置只有系统应用才会生效,非系统应用会被 PackageManagerService 调整为 0。
/**
* Adjusts the priority of the given intent filter according to policy.
*
*
* - The priority for non privileged applications is capped to '0'
* - The priority for protected actions on privileged applications is capped to '0'
* - The priority for unbundled updates to privileged applications is capped to the
* priority defined on the system partition
*
*
* NOTE: There is one exception. For security reasons, the setup wizard is
* allowed to obtain any priority on any action.
*/
private void adjustPriority(
List systemActivities, ActivityIntentInfo intent) {
...
final boolean privilegedApp =
((applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0);
if (!privilegedApp) {
// non-privileged applications can never define a priority >0
Slog.w(TAG, "Non-privileged app; cap priority to 0;"
+ " package: " + applicationInfo.packageName
+ " activity: " + intent.activity.className
+ " origPrio: " + intent.getPriority());
intent.setPriority(0);
return;
}
if (systemActivities == null) {
// the system package is not disabled; we're parsing the system partition
if (isProtectedAction(intent)) {
if (mDeferProtectedFilters) {
// We can't deal with these just yet. No component should ever obtain a
// >0 priority for a protected actions, with ONE exception -- the setup
// wizard. The setup wizard, however, cannot be known until we're able to
// query it for the category CATEGORY_SETUP_WIZARD. Which we can't do
// until all intent filters have been processed. Chicken, meet egg.
// Let the filter temporarily have a high priority and rectify the
// priorities after all system packages have been scanned.
mProtectedFilters.add(intent);
if (DEBUG_FILTERS) {
Slog.i(TAG, "Protected action; save for later;"
+ " package: " + applicationInfo.packageName
+ " activity: " + intent.activity.className
+ " origPrio: " + intent.getPriority());
}
return;
} else {
if (DEBUG_FILTERS && mSetupWizardPackage == null) {
Slog.i(TAG, "No setup wizard;"
+ " All protected intents capped to priority 0");
}
if (intent.activity.info.packageName.equals(mSetupWizardPackage)) {
if (DEBUG_FILTERS) {
Slog.i(TAG, "Found setup wizard;"
+ " allow priority " + intent.getPriority() + ";"
+ " package: " + intent.activity.info.packageName
+ " activity: " + intent.activity.className
+ " priority: " + intent.getPriority());
}
// setup wizard gets whatever it wants
return;
}
Slog.w(TAG, "Protected action; cap priority to 0;"
+ " package: " + intent.activity.info.packageName
+ " activity: " + intent.activity.className
+ " origPrio: " + intent.getPriority());
intent.setPriority(0);
return;
}
}
// privileged apps on the system image get whatever priority they request
return;
}
...
}
复制代码
在 adjustPriority 方法中,如果 packageName 为 mSetupWizardPackage 就不会调整其优先级,保持其 Manifest 中设置的优先级。mSetupWizardPackage 的值从 getSetupWizardPackageName() 方法中获取。
final @Nullable String mSetupWizardPackage;
...
public PackageManagerService(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {
...
mSetupWizardPackage = getSetupWizardPackageName();
...
}
private @Nullable String getSetupWizardPackageName() {
final Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_SETUP_WIZARD);
final List matches = queryIntentActivitiesInternal(intent, null,
MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE
| MATCH_DISABLED_COMPONENTS,
UserHandle.myUserId());
if (matches.size() == 1) {
return matches.get(0).getComponentInfo().packageName;
} else {
Slog.e(TAG, "There should probably be exactly one setup wizard; found " + matches.size()
+ ": matches=" + matches);
return null;
}
}
复制代码
所以 mSetupWizardPackage 就是有添加了 Category 为 android.intent.category.SETUP_WIZARD 的 Activity 的应用。
四、设置向导完成
既然设置向导的优先级高于 Launcher,那每次开机时不是都会先启动设置向导么,为什么设置向导完成后再次开机直接进入了 Launcher?
因为设置向导在最后退出时会禁用掉添加了 Category 为 Intent.CATEGORY_HOME 的 Activity,所以 ActivityManagerService 在 resolveActivityInfo() 获取匹配的应用时就不会获取到设置向导,直接获取到了 Launcher。
// remove this activity from the package manager.
PackageManager pm = getPackageManager();
ComponentName name = new ComponentName(this, DefaultActivity.class);
pm.setComponentEnabledSetting(name, PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP);
复制代码