persistent 属性的配置
<application
android:usesCleartextTraffic="true"
android:icon="@drawable/ic_launcher"
android:persistent="true"
android:label="@string/app_name">
persistent应用启动时机很早,早于开机广播的发送,以及桌面启动。
frameworks\base\services\core\java\com\android\server\am\ActivityManagerService.java
public void systemReady(final Runnable goingCallback)
{
...
synchronized (this) {
// Only start up encryption-aware persistent apps; once user is
// unlocked we'll come back around and start unaware apps
startPersistentApps(PackageManager.MATCH_DIRECT_BOOT_AWARE);
...
if (skipHome == false) {
//启动桌面
startHomeActivityLocked(currentUserId, "systemReady");
}
...
//这里发送完FINISH_BOOTING_MSG后才开始发送开机广播
postFinishBooting(false, true);
...
}
....
}
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) {
if (!"android".equals(app.packageName)) {
addAppLocked(app, null, false, null /* ABI override */,
ZYGOTE_POLICY_FLAG_BATCH_LAUNCH);
}
}
} catch (RemoteException ex) {
}
}
}
frameworks\base\services\core\java\com\android\server\pm\PackageManagerService.java
private @NonNull List<ApplicationInfo> getPersistentApplicationsInternal(int flags) {
final ArrayList<ApplicationInfo> finalList = new ArrayList<>();
// reader
synchronized (mLock) {
final int numPackages = mPackages.size();
final int userId = UserHandle.getCallingUserId();
for (int index = 0; index < numPackages; index++) {
final AndroidPackage p = mPackages.valueAt(index);
final boolean matchesUnaware = ((flags & MATCH_DIRECT_BOOT_UNAWARE) != 0)
&& !p.isDirectBootAware();
final boolean matchesAware = ((flags & MATCH_DIRECT_BOOT_AWARE) != 0)
&& p.isDirectBootAware();
if (p.isPersistent()
&& (!mSafeMode || p.isSystem())
&& (matchesUnaware || matchesAware)) {
PackageSetting ps = mSettings.getPackageLPr(p.getPackageName());
if (ps != null) {
ApplicationInfo ai = PackageInfoUtils.generateApplicationInfo(p, flags,
ps.readUserState(userId), userId, ps);
if (ai != null) {
finalList.add(ai);
}
}
}
}
}
return finalList;
}
在 PMS 中,有一个记录所有的程序包信息的哈希表(mPackages),每个表项中含有 ApplicationInfo 信息,该信息的 flags(int 型)数据中有一个专门的 bit 用于表示 persistent。getPersistentApplications () 函数会遍历这张表,找出所有 persistent 包,并返回 ArrayList<ApplicationInfo>。
从代码里可以看出,带 persistent 标志的系统应用(即 flags 中设置了 FLAG_SYSTEM)是一定会被选上的,但如果不是系统应用的话,则要进一步判断当前是否处于 “安全模式”,一旦处于安全模式,那么就算应用设置了 persistent 属性,也不会被选中。
persistent保活原理
persistent 应用会顽固地运行于系统之中,从系统一启动,一直到系统关机。
为了保证这种持久性,persistent 应用必须能够在异常出现时,自动重新启动。在 Android 里是这样实现的。每个 ActivityThread 中会有一个专门和 AMS 通信的 binder 实体 final ApplicationThread mAppThread。这个实体在 AMS 中对应的代理接口为 IApplicationThread。
当 AMS 执行到 attachApplicationLocked () 时,会针对目标用户进程的 IApplicationThread 接口,注册一个 binder 讣告监听器,一旦日后用户进程意外挂掉,AMS 就能在第一时间感知到,并采取相应的措施。如果 AMS 发现意外挂掉的应用是 persistent 的,它会尝试重新启动这个应用。
frameworks\base\core\java\android\app\ActivityThread.java
@UnsupportedAppUsage
final ApplicationThread mAppThread = new ApplicationThread();
frameworks\base\services\core\java\com\android\server\am\ActivityManagerService.java
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, null);
}
}
}
在attachApplicationLocked方法中 注册 binder 讣告监听器,当其监听的 binder 实体死亡时,系统会回调 AppDeathRecipient 的 binderDied ()。
// Tell the process all about itself.
if (DEBUG_ALL) Slog.v(
TAG, "Binding process pid " + pid + " to record " + app);
final String processName = app.processName;
try {
AppDeathRecipient adr = new AppDeathRecipient(
app, pid, thread);
thread.asBinder().linkToDeath(adr, 0);
app.setDeathRecipient(adr);
} catch (RemoteException e) {
app.resetPackageList(mProcessStats);
mProcessList.startProcessLocked(app,
new HostingRecord("link fail", processName),
ZYGOTE_POLICY_FLAG_EMPTY);
return false;
}
调用关系如下:
binderDied--》appDiedLocked--》handleAppDiedLocked--》cleanUpApplicationRecordLocked--》startProcessLocked
一般情况下,当一个应用进程挂掉后,AMS 当然会清理掉其对应的 ProcessRecord,这就是 cleanUpApplicationRecordLocked () 的主要工作。对于 persistent 应用,cleanUpApplicationRecordLocked () 会尝试再次启动对应的应用进程。
/**
* Main code for cleaning up a process when it has gone away. This is
* called both as a result of the process dying, or directly when stopping
* a process when running in single process mode.
*
* @return Returns true if the given process has been restarted, so the
* app that was passed in must remain on the process lists.
*/
@GuardedBy("this")
final boolean cleanUpApplicationRecordLocked(ProcessRecord app, int pid,
boolean restarting, boolean allowRestart, int index, boolean replacingPid,
boolean fromBinderDied) {
boolean restart;
synchronized (mProcLock) {
if (index >= 0) {
removeLruProcessLocked(app);
ProcessList.remove(pid);
}
// We don't want to unlinkDeathRecipient immediately, if it's not called from binder
// and it's not isolated, as we'd need the signal to bookkeeping the dying process list.
restart = app.onCleanupApplicationRecordLSP(mProcessStats, allowRestart,
fromBinderDied || app.isolated /* unlinkDeath */);
// Cancel pending frozen task if there is any.
mOomAdjuster.mCachedAppOptimizer.unscheduleFreezeAppLSP(app);
}
mAppProfiler.onCleanupApplicationRecordLocked(app);
skipCurrentReceiverLocked(app);
updateProcessForegroundLocked(app, false, 0, false);
mServices.killServicesLocked(app, allowRestart);
mPhantomProcessList.onAppDied(pid);
// If the app is undergoing backup, tell the backup manager about it
final BackupRecord backupTarget = mBackupTargets.get(app.userId);
if (backupTarget != null && pid == backupTarget.app.getPid()) {
if (DEBUG_BACKUP || DEBUG_CLEANUP) Slog.d(TAG_CLEANUP, "App "
+ backupTarget.appInfo + " died during backup");
mHandler.post(new Runnable() {
@Override
public void run(){
try {
IBackupManager bm = IBackupManager.Stub.asInterface(
ServiceManager.getService(Context.BACKUP_SERVICE));
bm.agentDisconnectedForUser(app.userId, app.info.packageName);
} catch (RemoteException e) {
// can't happen; backup manager is local
}
}
});
}
mProcessList.scheduleDispatchProcessDiedLocked(pid, app.info.uid);
// If this is a preceding instance of another process instance
allowRestart = mProcessList.handlePrecedingAppDiedLocked(app);
// If the caller is restarting this app, then leave it in its
// current lists and let the caller take care of it.
if (restarting) {
return false;
}
if (!app.isPersistent() || app.isolated) {
if (DEBUG_PROCESSES || DEBUG_CLEANUP) Slog.v(TAG_CLEANUP,
"Removing non-persistent process during cleanup: " + app);
if (!replacingPid) {
mProcessList.removeProcessNameLocked(app.processName, app.uid, app);
}
mAtmInternal.clearHeavyWeightProcessIfEquals(app.getWindowProcessController());
} else if (!app.isRemoved()) {
// This app is persistent, so we need to keep its record around.
// If it is not already on the pending app list, add it there
// and start a new process for it.
if (mPersistentStartingProcesses.indexOf(app) < 0) {
mPersistentStartingProcesses.add(app);
restart = true;
}
}
if ((DEBUG_PROCESSES || DEBUG_CLEANUP) && mProcessesOnHold.contains(app)) Slog.v(
TAG_CLEANUP, "Clean-up removing on hold: " + app);
mProcessesOnHold.remove(app);
mAtmInternal.onCleanUpApplicationRecord(app.getWindowProcessController());
mProcessList.noteProcessDiedLocked(app);
if (restart && allowRestart && !app.isolated) {
// We have components that still need to be running in the
// process, so re-launch it.
if (index < 0) {
ProcessList.remove(pid);
}
// Remove provider publish timeout because we will start a new timeout when the
// restarted process is attaching (if the process contains launching providers).
mHandler.removeMessages(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG, app);
mProcessList.addProcessNameLocked(app);
app.setPendingStart(false);
mProcessList.startProcessLocked(app, new HostingRecord("restart", app.processName),
ZYGOTE_POLICY_FLAG_EMPTY);
return true;
} else if (pid > 0 && pid != MY_PID) {
// Goodbye!
removePidLocked(pid, app);
mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid);
if (app.isolated) {
mBatteryStatsService.removeIsolatedUid(app.uid, app.info.uid);
}
app.setPid(0);
}
return false;
}
扩展
在 AMS 中,有一个 isAllowedWhileBooting () 函数 ,persistent 属性设为 true 的应用,允许在 boot 的过程中启动的。
boolean isAllowedWhileBooting(ApplicationInfo ai) {
return (ai.flags&ApplicationInfo.FLAG_PERSISTENT) != 0;
}
frameworks\base\services\core\java\com\android\server\am\ProcessList.java
startProcessLocked方法
// If the system is not ready yet, then hold off on starting this
// process until it is.
if (!mService.mProcessesReady
&& !mService.isAllowedWhileBooting(info)
&& !allowWhileBooting) {
if (!mService.mProcessesOnHold.contains(app)) {
mService.mProcessesOnHold.add(app);
}
if (DEBUG_PROCESSES) Slog.v(TAG_PROCESSES,
"System not ready, putting on hold: " + app);
checkSlow(startTime, "startProcess: returning with proc on hold");
return app;
}
checkSlow(startTime, "startProcess: stepping in to startProcess");
final boolean success =
startProcessLocked(app, hostingRecord, zygotePolicyFlags, abiOverride);
checkSlow(startTime, "startProcess: done starting proc!");
return success ? app : null;
当系统已经处于以下几种情况时,多参数的 startProcessLocked () 会进一步调用另一个只有三个参数的 startProcessLocked ():
1)系统已经处于 ready 状态;
2)想要启动 persistent 应用;
3)参数中明确指定可以在 boot 过程中启动应用。
一般情况下,当 AMS 调用 startProcessLocked () 时,传入的 allowWhileBooting 参数都为 false。比如说,当系统需要启动 “某个 content provider 或者某个 service 或者某个特定 activity” 时,此时传给 startProcessLocked () 的 allowWhileBooting 参数是写死为 false 的。只有一种特殊情况下会在该参数中传入 true,那就是当系统发出的广播 intent 中携带有 Intent.FLAG_RECEIVER_BOOT_UPGRADE 标记时,此时允许在系统未 ready 时,启动接受广播的目标进程。