Bootloader 系統引導android
啓動 Linux 內核app
啓動 init 進程ide
啓動 Zygote 進程ui
啓動 SystemServer 進程this
SystemServer 會在 startBootstrapServices() 方法中會啓動 ActivityManagerService 。spa
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() 方法。rest
public void systemReady(final Runnable goingCallback) {
...
synchronized (this) {
...
startHomeActivityLocked(currentUserId, "systemReady");
...
}
...
}
複製代碼
startHomeActivityLocked() 方法中會獲取到 Action 爲 Intent.ACTION_MAIN,Category 爲 Intent.CATEGORY_HOME 的 Intent,根據該 Intent 獲取到符合條件的應用,並判斷該應用是否已經啓動,沒有啓動則啓動該應用。code
...
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。component
<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() 方法中。
Manifest 中 Activity 的 intent-filter 的優先級設置只有系統應用纔會生效,非系統應用會被 PackageManagerService 調整爲 0。
/** * Adjusts the priority of the given intent filter according to policy. * <p> * <ul> * <li>The priority for non privileged applications is capped to '0'</li> * <li>The priority for protected actions on privileged applications is capped to '0'</li> * <li>The priority for unbundled updates to privileged applications is capped to the * priority defined on the system partition</li> * </ul> * <p> * <em>NOTE:</em> There is one exception. For security reasons, the setup wizard is * allowed to obtain any priority on any action. */
private void adjustPriority( List<PackageParser.Activity> 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<ResolveInfo> 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);
複製代碼