Android 设置向导启动分析
一、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() {@Overridepublic 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"/>
...<applicationandroid: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" ><activityandroid: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><activityandroid: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.* <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 >0Slog.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 partitionif (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 wantsreturn;}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 requestreturn;}...
}
复制代码
在 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);
复制代码
Android 设置向导启动分析相关推荐
- Android DHCP 启动分析【2】
2019独角兽企业重金招聘Python工程师标准>>> 一.DHCP client 和 dhcpcd server直接的信息交互: Client 和server 通过property ...
- android挂载usb设备,android usb挂载分析---MountService启动
在android usb挂载分析----vold启动,我们的vold模块已经启动了,通信的机制也已经建立起来了,接下来我们分析一下MountService的启动,也就是我们FrameWork层的启动, ...
- 【Android 安全】DEX 加密 ( Application 替换 | Android 应用启动原理 | ActivityThread 后续分析 | Application 替换位置 )
文章目录 一.ActivityThread 后续分析 二.ActivityThread 相关源码 三.Application 替换位置 dex 解密时 , 需要将 代理 Application 替换为 ...
- 【Android 性能优化】应用启动优化 ( 安卓应用启动分析 | Launcher 应用启用普通安卓应用 | 应用进程分析 )
文章目录 一. Launcher 应用 startActivitySafely 方法分析 二. Launcher 中的 startActivity(View v, Intent intent, Obj ...
- Android 启动分析 1
1.概述 Android虽然被称作一种操作系统,其实它仍然使用的Linux的kernel.所以本质上可以说,Android是一个适用于移动设备的Linux发行版.也就是说,之前的分析Linux内核的经 ...
- 图解Android - Zygote, System Server 启动分析
Init 是所有Linux程序的起点,而Zygote于Android,正如它的英文意思,是所有java程序的'孵化池'(玩过星际虫族的兄弟都晓得的).用ps 输出可以看到 >adb shell ...
- 分析启动耗时 android,Android app启动耗时分析
首先编译你的程序,打开Android Studio里面的Android Monitor,找到下图的按钮 &amp;amp;amp;amp;lt;img src="//bbsm ...
- android usb挂载分析---MountService启动
在android usb挂载分析----vold启动,我们的vold模块已经启动了,通信的机制也已经建立起来了,接下来我们分析一下MountService的启动,也就是我们FrameWork层的启动, ...
- 【SemiDrive源码分析】【X9芯片启动流程】30 - AP1 Android Kernel 启动流程 start_kernel 函数详细分析(一)
[SemiDrive源码分析][X9芯片启动流程]30 - AP1 Android Kernel 启动流程 start_kernel 函数详细分析(一) 一.Android Kernel 启动流程分析 ...
最新文章
- 深度睡眠中,记忆如何被保持?
- Web安全测试检查单
- Select 标签可输入
- 程序进入后台继续执行
- 【Compiling Swift source files】编译很慢;
- 实例解析网络编程中的另类内存泄漏
- primefaces教程_Primefaces FileUpload组件示例教程
- 一元线性回归(Linear Regression)
- 肿瘤浸润淋巴细胞的扩增方法学评估
- 3D游戏编程与设计4——游戏对象与图形基础
- 通俗地讲一下庞加莱猜想是怎么回事(from 鼓浪)
- 第八章:加载Maya2011模型
- 网页加速之Link prefetching
- 双十一之后,留给证券区块链转型的时间不多了……
- C语言头文件.h互相包含所引发的一系列错误C2143之类的解决方法
- VSCode安装教程
- 小数乘法的C语言,小数乘法 5
- 正则表达式匹配——《剑指offer》
- EasyUI学习-如何使用jQuery EasyUI?
- C语言 记忆测试(Memory Test)小游戏的实现