说说Android利用的persistent属性
侯 亮
1 启动persistent利用
在Android系统中,有1种永久性利用。它们对应的AndroidManifest.xml文件里,会将persistent属性设为true,比如:
<application android:name="PhoneApp"
android:persistent="true"
android:label="@string/dialerIconLabel"
android:icon="@drawable/ic_launcher_phone">
在系统启动之时,AMS的systemReady()会加载所有persistent为true的利用。
public void systemReady(final Runnable goingCallback)
{
. . . . . .
. . . . . .
try{
List apps = AppGlobals.getPackageManager().
getPersistentApplications(STOCK_PM_FLAGS);
if(apps != null)
{
intN = apps.size();
inti;
for(i=0; i<N; i++)
{
ApplicationInfo info = (ApplicationInfo)apps.get(i);
if(info != null&&
!info.packageName.equals("android"))
{
addAppLocked(info,false);
}
}
}
}
catch(RemoteException ex) {
// pm is in same process, this will never happen.
}
其中的STOCK_PM_FLAGS的定义以下:
// The flags that are set for all calls we make to the package manager.
static final int STOCK_PM_FLAGS = PackageManager.GET_SHARED_LIBRARY_FILES;
上面代码中的getPersistentApplications()函数的定义以下:
public List<ApplicationInfo> getPersistentApplications(int flags)
{
final ArrayList<ApplicationInfo> finalList = new ArrayList<ApplicationInfo>();
// reader
synchronized (mPackages)
{
final Iterator<PackageParser.Package> i = mPackages.values().iterator();
final int userId = UserId.getCallingUserId();
while (i.hasNext())
{
final PackageParser.Package p = i.next();
if (p.applicationInfo != null
&& (p.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) != 0
&& (!mSafeMode || isSystemApp(p)))
{
PackageSetting ps = mSettings.mPackages.get(p.packageName);
finalList.add(PackageParser.generateApplicationInfo(p, flags,
ps != null ? ps.getStopped(userId) : false,
ps != null ? ps.getEnabled(userId) : COMPONENT_ENABLED_STATE_DEFAULT,
userId));
}
}
}
return finalList;
}
在PKMS中,有1个记录所有的程序包信息的哈希表(mPackages),每一个表项中含有ApplicationInfo信息,该信息的flags(int型)数据中有1个专门的bit用于表示persistent。getPersistentApplications()函数会遍历这张表,找出所有persistent包,并返回ArrayList<ApplicationInfo>。
从代码里可以看出,带persistent标志的系统利用(即flags中设置了FLAG_SYSTEM)是1定会被选上的,但如果不是系统利用的话,则要进1步判断当前是不是处于“安全模式”,1旦处于安全模式,那末就算利用设置了persistent属性,也不会被选中。
随后systemReady()开始遍历选中的ApplicationInfo,并对包名不为“android”的结点履行addAppLocked()。addAppLocked()的代码以下:
<pre name="code" class="java">final ProcessRecord addAppLocked(ApplicationInfo info, boolean isolated)
{
ProcessRecord app;
if (!isolated) {
app = getProcessRecordLocked(info.processName, info.uid);
} else {
app = null;
}
if (app == null) {
app = newProcessRecordLocked(null, info, null, isolated);
mProcessNames.put(info.processName, app.uid, app);
if (isolated) {
mIsolatedProcesses.put(app.uid, app);
}
updateLruProcessLocked(app, true, true);
}
// This package really, really can not be stopped.
try {
AppGlobals.getPackageManager().setPackageStoppedState(
info.packageName, false, UserId.getUserId(app.uid));
} catch (RemoteException e) {
} catch (IllegalArgumentException e) {
Slog.w(TAG, "Failed trying to unstop package "
+ info.packageName + ": " + e);
}
if ((info.flags&(ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PERSISTENT))
== (ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PERSISTENT)) {
app.persistent = true;
app.maxAdj = ProcessList.PERSISTENT_PROC_ADJ;
}
if (app.thread == null && mPersistentStartingProcesses.indexOf(app) < 0) {
mPersistentStartingProcesses.add(app);
startProcessLocked(app, "added application", app.processName);
}
return app;
}
在AMS中,所谓的“add App”主要是指“添加1个与App进程对应的ProcessRecord节点”。固然,如果该节点已添加过了,那末是不会重复添加的。在添加节点的动作完成以后,addAppLocked()还会检查App进程是不是已启动好了,如果还没有开始启动,此时就会调用startProcessLocked()启动这个进程。既然addAppLocked()试图确认App“正在正常运作”或“将被正常启动”,那末其对应的package就不可能处于stopped状态,这就是上面代码调用setPackageStoppedState(...,false,...)的意思。
现在,我们就清楚了,那些persistent属性为true的利用,基本上都是在系统启动伊始就启动起来的。
由于启动进程的进程是异步的,所以我们需要1个缓冲列表(即上面代码中的mPersistentStartingProcesses列表)来记录那些“正处于启动状态,而又没有启动终了的”ProcessRecord结点。1旦目标进程启动终了后,目标进程会attach系统,因而走到AMS的attachApplicationLocked(),在这个函数里,会把目标进程对应的ProcessRecord结点从mPersistentStartingProcesses缓冲列表里删除。
private final boolean attachApplicationLocked(IApplicationThread thread, intpid) {
// Find the application record that is being attached... either via
// the pid if we are running in multiple processes, or just pull the
// next app record if we are emulating process with anonymous threads.
ProcessRecord app;
. . . . . .
thread.asBinder().linkToDeath(adr,0);
. . . . . .
thread.bindApplication(processName, appInfo, providers,
app.instrumentationClass, profileFile, profileFd, profileAutoStop,
app.instrumentationArguments, app.instrumentationWatcher, testMode,
enableOpenGlTrace, isRestrictedBackupMode || !normalMode,
app.persistent,
newConfiguration(mConfiguration), app.compat,
getCommonServicesLocked(),
mCoreSettingsObserver.getCoreSettingsLocked());
. . . . . .
. . . . . .
// Remove this record from the list of starting applications.
mPersistentStartingProcesses.remove(app);
. . . . . .
2 如何保证利用的持久性(persistent)
我们知道,persistent1词的意思是“持久”,那末persistent利用的意思又是甚么呢?简单地说,这类利用会固执地运行于系统当中,从系统1启动,1直到系统关机。
为了保证这类持久性,persistent利用必须能够在异常出现时,自动重新启动。在Android里是这样实现的。每一个ActivityThread中会有1个专门和AMS通讯的binder实体――final ApplicationThread mAppThread。这个实体在AMS中对应的代理接口为IApplicationThread。
当AMS履行到attachApplicationLocked()时,会针对目标用户进程的IApplicationThread接口,注册1个binder讣告监听器,1旦往后用户进程意外挂掉,AMS就可以在第1时间感知到,并采取相应的措施。如果AMS发现意外挂掉的利用是persistent的,它会尝试重新启动这个利用。
注册讣告监听器的代码以下:
AppDeathRecipient adr = new AppDeathRecipient(app, pid, thread);
thread.asBinder().linkToDeath(adr,0);
app.deathRecipient = adr;
其中的thread就是IApplicationThread代理。
AppDeathRecipient的定义以下:
private final class AppDeathRecipient implementsIBinder.DeathRecipient
{
final ProcessRecord mApp;
final int mPid;
final IApplicationThread mAppThread;
AppDeathRecipient(ProcessRecord app, intpid,
IApplicationThread thread)
{
if(localLOGV)
Slog.v(TAG,"New death recipient " + this
+" for thread " + thread.asBinder());
mApp = app;
mPid = pid;
mAppThread = thread;
}
publicvoidbinderDied()
{
if(localLOGV)
Slog.v(TAG,"Death received in " + this
+" for thread " + mAppThread.asBinder());
synchronized(ActivityManagerService.this)
{
appDiedLocked(mApp, mPid, mAppThread);
}
}
}
当其监听的binder实体死亡时,系统会回调AppDeathRecipient的binderDied()。这个回调函数会展转重启persistent利用,调用关系以下:
1般情况下,当1个利用进程挂掉后,AMS固然会清算掉其对应的ProcessRecord,这就是cleanUpApplicationRecordLocked()的主要工作。但是,对persistent利用,cleanUpApplicationRecordLocked()会尝试再次启动对应的利用进程。代码截选以下:
private final void cleanUpApplicationRecordLocked(ProcessRecord app,
boolean restarting,
boolean allowRestart,int index)
{
. . . . . .
. . . . . .
if (!app.persistent || app.isolated)
{
. . . . . .
mProcessNames.remove(app.processName, app.uid);
mIsolatedProcesses.remove(app.uid);
. . . . . .
}
else if(!app.removed)
{
if(mPersistentStartingProcesses.indexOf(app) < 0) {
mPersistentStartingProcesses.add(app);
restart = true;
}
}
. . . . . .
. . . . . .
if (restart && !app.isolated)
{
mProcessNames.put(app.processName, app.uid, app);
startProcessLocked(app,"restart", app.processName);
}
else if(app.pid > 0&& app.pid != MY_PID)
{
. . . . . .
}
. . . . . .
}
现在我们可以画1张关于“启动persistent利用”的示意图:
3 补充知识点
3.1 persistent利用可以在系统未准备好时启动
在AMS中,有1个isAllowedWhileBooting()函数,其代码以下:
boolean isAllowedWhileBooting(ApplicationInfo ai)
{
return (ai.flags & ApplicationInfo.FLAG_PERSISTENT) != 0;
}
从这个函数可以看到,将persistent属性设为true的利用,是允许在boot的进程中启动的。我们可以查看前文提到的startProcessLocked()函数:
final ProcessRecord startProcessLocked(String processName,
ApplicationInfo info, boolean knownToBeDead,
int intentFlags,
String hostingType, ComponentName hostingName,
boolean allowWhileBooting,
boolean isolated)
{
ProcessRecord app;
if(!isolated)
{
app = getProcessRecordLocked(processName, info.uid);
}
else
{
// If this is an isolated process, it can't re-use an existing process.
app = null;
}
. . . . . .
. . . . . .
if(!mProcessesReady
&& !isAllowedWhileBooting(info)
&& !allowWhileBooting) {
if(!mProcessesOnHold.contains(app)) {
mProcessesOnHold.add(app);
}
if(DEBUG_PROCESSES) Slog.v(TAG, "System not ready, putting on hold: " + app);
return app;
}
startProcessLocked(app, hostingType, hostingNameStr);
return (app.pid != 0) ? app : null;
}
其中的最后几句可以改写为以下更容易理解的情势:
if (mProcessesReady || isAllowedWhileBooting(info) || allowWhileBooting)
{
startProcessLocked(app, hostingType, hostingNameStr);
return (app.pid != 0) ? app : null;
}
else
{
. . . . . .
returnapp;
}
也就是说,当系统已处于以下几种情况时,多参数的startProcessLocked()会进1步调用另外一个只有3个参数的startProcessLocked():
1)系统已处于ready状态;
2)想要启动persistent利用;
3)参数中明确指定可以在boot进程中启动利用。
补充说1下,1般情况下,当AMS调用startProcessLocked()时,传入的allowWhileBooting参数都为false。比如说,当系统需要启动“某个content provider或某个service或某个特定activity”时,此时传给startProcessLocked()的allowWhileBooting参数是写死为false的。只有1种特殊情况下会在该参数中传入true,那就是当系统发出的广播intent中携带有Intent.FLAG_RECEIVER_BOOT_UPGRADE标记时,此时允许在系统未ready时,启动接受广播的目标进程。
4 结束
有关Android利用的persistent属性,我们就先说这么多。希望对大家有点儿帮助。