PMS/AMS剖析之应用开机自启动
一、背景介绍
一般而言,手机中某些重要的系统应用都会要求开机之后自动启动,Android中提供一种标签来表明当前的应用需要开机自动启动,这个标签就是android:persistent=true
AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="......"
android:versionCode="30"
android:versionName="2.0"
android:sharedUserId="android.uid.system">
<application
android:name="......"
android:allowBackup="true"
android:persistent="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
</application>
</manifest>
可以看到其中使用了android:persistent="true",本文主要内容如下,其实从本文的标题也能看出来,这是PMS与AMS通力合作的结果,PMS负责解析工作,AMS在启动应用的时候回用到解析的结果,用于实现自启动的效果,接下来分别从两个大角度阐释一下:
1.解析android:persistent="true"
2.应用如何自启动
二、解析android:persistent="true"
讲解之前,大家可以参考一下我之前的文章《PackageManagerService架构剖析开篇》中的1.2.5小节——扫描应用app和应用目录下的lib,主要就是在扫描这个app上面,扫描app流程中,会解析出app文件,app文件包含资源文件、AndroidManifest.xml、assets等文件,这里关于android:persistent="true"的解析,就是在AndroidManifest.xml中,而解析AndroidManifest.xml的代码是在PackageParser.java中,代码和流程总结如下:
执行流程如下:
本文我们主要关注的核心要点是如何解析application节点中的属性,可以先check一下具体的代码,加深理解。
PackageParser.java
private boolean parseBaseApplication(Package owner, Resources res,
XmlResourceParser parser, int flags, String[] outError)
throws XmlPullParserException, IOException {
final ApplicationInfo ai = owner.applicationInfo;
final String pkgName = owner.applicationInfo.packageName;
TypedArray sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifestApplication);
//......
}
这个com.android.internal.R.styleable.AndroidManifestApplication存储的地方在frameworks/base/core/res/res/values/attrs_manifest.xml,这里面包含的属性很多,本文重点提一下android:persistent,如下:
frameworks/base/core/res/res/values/attrs_manifest.xml
<declare-styleable name="AndroidManifestApplication" parent="AndroidManifest">
<!-- 省略了很多-->
<attr name="persistent" format="boolean" />
<attr name="persistentWhenFeatureAvailable" format="string" />
<!-- 省略了很多-->
</declare-styleable>
这个android:persistent解析的具体地方是:
PackageParser.java
if ((flags&PARSE_IS_SYSTEM) != 0) {
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_persistent,
false)) {
// Check if persistence is based on a feature being present
final String requiredFeature = sa.getNonResourceString(
com.android.internal.R.styleable.
AndroidManifestApplication_persistentWhenFeatureAvailable);
if (requiredFeature == null || mCallback.hasFeature(requiredFeature)) {
ai.flags |= ApplicationInfo.FLAG_PERSISTENT;
}
}
}
这儿发现符合android:persistent="true"条件之后,persistentWhenFeatureAvailable属性只是标识当前的frature是否可用,一般忽略这个属性,其实判断的时候也没有起到作用. 可以执行位操作:ai.flags |= ApplicationInfo.FLAG_PERSISTENT;
ai是ApplicationInfo的实例,说明定义了android:persistent="true"的应用applicationInfo的flag中的有Application.FLAG_PERSISTENT
public static final int FLAG_PERSISTENT = 1<<3;
到这儿android:persistent="true"解析工作已经完成,接下来需要看看这个flag标识位在应用启动的时候如何起作用的。
三、应用如何自启动
我们知道Android中管理Activity的是AMS,而Activity又是应用界面的承载体,Activity是和用户交互的组件,那么讲解应用启动,必定要谈到AMS,而android:persistent="true"标识着系统的自启动应用,那么它的优先级一定非常高,需要在系统启动的时候就要启动这些应用,那么我们只需要check一下AMS的启动流程,启动系统自启动应用的地方一定在靠前的地方。
之前在《ActivityManagerService架构剖析之Activity状态》中的--->1.1 AMS 启动流程 谈到了AMS的启动流程, 大家如果忘记的话可以对着参考一下,在SystemServer中反射构造了AMS对象之后,调用的靠前的AMS方法是什么?这个很重要,因为优先级高的系统自启动应用就在这儿被触发了。下面只关注系统自启动应用的启动流程,大家参考一下。
下面结合代码,按照执行流程,和大家详细说一下persistent app的启动流程,中间涉及到很多判断条件,掌握这些判断条件,对理解自启动应用有很大的帮助。
3.1 获取persistent 应用
这是在执行startPersistentApps之后,获取当前解析出来的persistent 应用,这里直接调用PMS,通过下面的代码调用:
ActivityManagerService.java
private void startPersistentApps(int matchFlags) {
if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL) return;
synchronized (this) {
try {
final List<ApplicationInfo> apps = AppGlobals.getPackageManager()
.getPersistentApplications(STOCK_PM_FLAGS | matchFlags).getList();
for (ApplicationInfo app : apps) {
//......
}
} catch (RemoteException ex) {
}
}
}
直接通过AppGlobals.getPackageManager()可以获取PMS的实例,通过ActivityThread获取PMS服务。
AppGlobals.java
public class AppGlobals {
//......
/**
* Return the raw interface to the package manager.
* @return The package manager.
*/
public static IPackageManager getPackageManager() {
return ActivityThread.getPackageManager();
}
//......
}
ActivityThread.java
public static IPackageManager getPackageManager() {
if (sPackageManager != null) {
return sPackageManager;
}
IBinder b = ServiceManager.getService("package");
sPackageManager = IPackageManager.Stub.asInterface(b);
return sPackageManager;
}
到PMS中查看一下获取persistent 应用的具体执行方法。
PackageManagerService.java
public @NonNull ParceledListSlice<ApplicationInfo> getPersistentApplications(int flags) {
if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
return ParceledListSlice.emptyList();
}
return new ParceledListSlice<>(getPersistentApplicationsInternal(flags));
}
private @NonNull List<ApplicationInfo> getPersistentApplicationsInternal(int flags) {
final ArrayList<ApplicationInfo> finalList = new ArrayList<ApplicationInfo>();
// reader
synchronized (mPackages) {
final Iterator<PackageParser.Package> i = mPackages.values().iterator();
final int userId = UserHandle.getCallingUserId();
while (i.hasNext()) {
final PackageParser.Package p = i.next();
if (p.applicationInfo == null) continue;
final boolean matchesUnaware = ((flags & MATCH_DIRECT_BOOT_UNAWARE) != 0)
&& !p.applicationInfo.isDirectBootAware();
final boolean matchesAware = ((flags & MATCH_DIRECT_BOOT_AWARE) != 0)
&& p.applicationInfo.isDirectBootAware();
if ((p.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) != 0
&& (!mSafeMode || isSystemApp(p))
&& (matchesUnaware || matchesAware)) {
PackageSetting ps = mSettings.mPackages.get(p.packageName);
if (ps != null) {
ApplicationInfo ai = PackageParser.generateApplicationInfo(p, flags,
ps.readUserState(userId), userId);
if (ai != null) {
finalList.add(ai);
}
}
}
}
}
return finalList;
}
重要的只有这一句:
if ((p.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) != 0
&& (!mSafeMode || isSystemApp(p))
&& (matchesUnaware || matchesAware))
这个判断条件,拆开来理解就是
1.带上persistent标识,并且是系统应用,一定可以。
2.如果当前处于安全模式,普通应用的话带上persistent标识也是不可以的。
3.2 设置当前的应用不能被停止
final ProcessRecord addAppLocked(ApplicationInfo info, String customProcess, boolean isolated,
String abiOverride) {
//......
AppGlobals.getPackageManager().setPackageStoppedState(
info.packageName, false, UserHandle.getUserId(app.uid));
//......
}
存储一个标识量,表明当前的packageName的应用是不能被停掉的,这里大家了解即可,这些标识量后面杀掉应用的时候会用到。
3.3 添加应用进程的节点
addAppLocked(...)
3.3.1 当前进程是否存在
首先判断当前的app所在的进程是否存在,如果不存在,创建一个新的ProcessRecord节点。
3.3.2 更新ProcessRecord
更新当前的processRecord,例如LruProcess结构中的信息,当前进程优先级也需要更新一下。
3.3.3 设置persistent属性
设置persistent属性,PMS之前将AndroidManifest.xml中的android:persistent="true"属性解析出来,现在讲这个属性更新到AMS中的数据结构中.
ActivityManagerService.java
addAppLocked(...)
if ((info.flags & PERSISTENT_MASK) == PERSISTENT_MASK) {
app.persistent = true;
app.maxAdj = ProcessList.PERSISTENT_PROC_ADJ;
}
这个app.persistent属性在后续的判断中会起到作用.
3.3.4 创建进程
startProcessLocked(...)会创建新的proces,这里面的代码比较多,但是都是判断标识的,这儿不作赘述,直入主题,下面:
ActivityManagerService.java
final ProcessRecord addAppLocked(ApplicationInfo info, String customProcess, boolean isolated,
String abiOverride) {
ProcessRecord app;
if (app.thread == null && mPersistentStartingProcesses.indexOf(app) < 0) {
mPersistentStartingProcesses.add(app);
app.callerPackage = "android";
startProcessLocked(app, "added application",
customProcess != null ? customProcess : app.processName, abiOverride,
null /* entryPoint */, null /* entryPointArgs */);
}
}
这儿的mPersistentStartingProcesses是即将启动的persistent应用,但是目前这些app还没有启动,需要暂时存在list中,等到app确定启动了,才remove掉,remove的代码在下面.
ActivityManagerService.java
startProcessLocked(...)
if (entryPoint == null) entryPoint = "android.app.ActivityThread";
//......s
ProcessStartResult startResult;
if (hostingType.equals("webview_service")) {
startResult = startWebView(entryPoint,
app.processName, uid, uid, gids, debugFlags, mountExternal,
app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
app.info.dataDir, null, entryPointArgs);
} else {
startResult = Process.start(entryPoint,
app.processName, uid, uid, gids, debugFlags, mountExternal,
app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
app.info.dataDir, invokeWith, entryPointArgs);
}
这个Process.start中通过socket和zygote进程通信,然后zygote fork一个新的进程,这里划重点:entryPoint变量指向ActivityThread.java,熟悉源码的同学应该知道,这个ActivityThread-->main(...)就是应用程序的入口,千万别在说Activity-->onCreate(...)是应用程序的入口了.
3.3.5 attach应用程序
ActivityThread类非常重要,这个我会专门写一篇文章讲解,本文是探讨persistent属性的,不展开描述了,要将和perisistent相关的知识.
进入ActivityThread-->main(...)会执行其中的attach(...)方法,这个attach方法其中包含一些执行步骤.
attach-application流程.jpg
核心代码如下:
ActivityManagerService.java
private final boolean attachApplicationLocked(IApplicationThread thread,
int pid) {
ProcessRecord app;
//......
final String processName = app.processName;
try {
AppDeathRecipient adr = new AppDeathRecipient(
app, pid, thread);
thread.asBinder().linkToDeath(adr, 0);
app.deathRecipient = adr;
} catch (RemoteException e) {
app.resetPackageList(mProcessStats);
startProcessLocked(app, "link fail", processName);
return false;
}
//......
if (app.instr != null) {
thread.bindApplication(processName, appInfo, providers,
app.instr.mClass,
profilerInfo, app.instr.mArguments,
app.instr.mWatcher,
app.instr.mUiAutomationConnection, testMode,
mBinderTransactionTrackingEnabled, enableTrackAllocation,
isRestrictedBackupMode || !normalMode, app.persistent,
new Configuration(getGlobalConfiguration()), app.compat,
getCommonServicesLocked(app.isolated),
mCoreSettingsObserver.getCoreSettingsLocked(),
buildSerial);
} else {
thread.bindApplication(processName, appInfo, providers, null, profilerInfo,
null, null, null, testMode,
mBinderTransactionTrackingEnabled, enableTrackAllocation,
isRestrictedBackupMode || !normalMode, app.persistent,
new Configuration(getGlobalConfiguration()), app.compat,
getCommonServicesLocked(app.isolated),
mCoreSettingsObserver.getCoreSettingsLocked(),
buildSerial);
}
//......
mPersistentStartingProcesses.remove(app);
//......
}
mPersistentStartingProcesses.remove(app);将目标进程从对应的persistent缓冲列表中删除,此时persistent应用已经启动完成,这个mPersistentStartingProcesses就是persistent应用启动缓存列表,之前解析出persistent标识的时候将符合条件的应用存入List中,现在应用启动了,需要删除一下.
了解了启动persistent应用的整体流程,接下来我们需要知道Android中是如何保持persistent应用持久性的.
3.4 如何保持persistent应用持久性
什么叫persistent应用,就是能持久运行在系统中的应用,这些应用从系统启动的时候开始运行,一直到系统关机.当发生异常的时候,这些persistent应用必须能重新启动,启动的地方我们已经知道了,这个应用会在系统启动的时候就启动,但是如果发生异常,或者被杀死,必须保证这些应用还能重新启动.
Android中有一个死亡监听器,一旦用户进程挂掉,我们能感知到.
这个所谓的死亡监听器,在系统启动的时候就已经设置了,上面的 3.3.5 attach应用程序 的地方就是注册这个死亡监听器的地方,大家可以往上翻一下记录,如下代码:
AppDeathRecipient adr = new AppDeathRecipient(
app, pid, thread);
thread.asBinder().linkToDeath(adr, 0);
app.deathRecipient = adr;
这个AppDeathRecipient就是一个死亡监听器,如果当前进程挂掉或者发生其他的意外情况导致当前的进程无法继续运行,就会执行binderDied(...)方法标识"死亡",这时候我们可以在回调方法中接收到信息,然后执行相应的动作.
private final class AppDeathRecipient implements IBinder.DeathRecipient {
final ProcessRecord mApp;
final int mPid;
final IApplicationThread mAppThread;
AppDeathRecipient(ProcessRecord app, int pid,
IApplicationThread thread) {
if (DEBUG_ALL) Slog.v(
TAG, "New death recipient " + this
+ " for thread " + thread.asBinder());
mApp = app;
mPid = pid;
mAppThread = thread;
}
@Override
public void binderDied() {
if (DEBUG_ALL) Slog.v(
TAG, "Death received in " + this
+ " for thread " + mAppThread.asBinder());
synchronized(ActivityManagerService.this) {
appDiedLocked(mApp, mPid, mAppThread, true);
}
}
}
查看一下binderDied()的执行流程,可以更加全面的了解怎么保活.
binderDie执行流程.jpg
核心的执行方法在cleanUpApplicationRecordLocked(...)中,刨除细枝末节的代码.只留下核心的重启代码.
ActivityManagerService.java
private final boolean cleanUpApplicationRecordLocked(ProcessRecord app,
boolean restarting, boolean allowRestart, int index, boolean replacingPid) {
//......
app.unlinkDeathRecipient();
//......
if (!app.persistent || app.isolated) {
if (!replacingPid) {
removeProcessNameLocked(app.processName, app.uid, app);
}
//......
} else if (!app.removed) {
if (mPersistentStartingProcesses.indexOf(app) < 0) {
mPersistentStartingProcesses.add(app);
restart = true;
}
}
//......
if (restart && !app.isolated) {
// We have components that still need to be running in the
// process, so re-launch it.
if (index < 0) {
ProcessList.remove(app.pid);
}
addProcessNameLocked(app);
startProcessLocked(app, "restart", app.processName);
return true;
}
//......
}
通常情况下,一个应用程序挂掉后,AMS清理其相关的ProcessRecord,上面这个方法就负责执行这个动作,这儿有看到了判断条件,如果是persistent应用,则标识restart,会在下面重新启动.这儿看到了persistent保活的机制和原理.
四、总结
这里明确一下,只有系统应用使用persistent才可以起到长期保活的作用;不是系统应用,如果手机处于不安全的模式,换言之,手机被root了,persistent也可以对普通应用起作用,但是现在这种情况不太多了,所以这种保活的做法不适宜广泛推广.