Persistent flag 指的是 ApplicationInfo
类中的常量 FLAG_PERSISTENT (1<<3)
。如果一个应用是 persistent 的,则一直在运行状态,即使被 kill 也会被重新启动。SystemUI 就是 persistent 的应用,可以想象如果 SystemUI 停止运行了,StatusBar 和 NavigationBar 也就没有了,这肯定是不正常的。Persistent 属性如何设置,很简单,在 AndroidManifest.xml 的 application 标签中声明就可以。
<application
android:name=".SystemUIApplication"
android:persistent="true"
...
>
...
</application>
所有应用安装后会在 /data/system/packages.xml
中更新相关信息,其中 publicFlag 就是该应用的所拥有的 flags。可以把该值转成二进制后查看千位是否是 1 来判断该应用是否拥有 persistent 属性。
不是说只要在 AndroidManifest.xml 中声明 persistent 便可以拥有 persistent flag。Persistent flag 只有系统应用才会生效,而且是安装在 system 或 vendor 分区的系统应用。
ApplicationInfo.FLAG_PERSISTENT
是在 PackageParser
类的 parseBaseApplication()
方法中设置的。因为 Android 9.0 对 persistent flag 做了一些修改,所以下面分为 Android 8.1 和 Android 9.0 来分析源码。
scanDirTracedLI()
到 parseBaseApplication()
的调用链。对于 Android 8.1,能否设置 FLAG_PERSISTENT
有两个条件,第一个是 parseBaseApplication()
方法的参数 flags
中包含 PARSE_IS_SYSTEM
,第二个是 AndroidManifest 中声明了 persistent。
package android.content.pm;
public class PackageParser {
/**
* Parse the {@code application} XML tree at the current parse location in a
* <em>base APK</em> manifest.
* <p>
* When adding new features, carefully consider if they should also be
* supported by split APKs.
*/
private boolean parseBaseApplication(Package owner, Resources res,
XmlResourceParser parser, int flags, String[] outError)
throws XmlPullParserException, IOException {
...
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;
}}}
...
}}
参数 flags
是,PackageManagerService 中调用 scanDirTracedLI()
方法或调用 installPackageLI()
方法时传入的 parseFlags
参数。
在调用 installPackageLI()
时没用设置 PARSE_IS_SYSTEM
,而调用 scanDirTracedLI()
时会根据扫描的目录来设置的。
// Find base frameworks (resource packages without code).
scanDirTracedLI(frameworkDir, mDefParseFlags
| PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR
| PackageParser.PARSE_IS_PRIVILEGED,
scanFlags | SCAN_NO_DEX, 0);
// Collected privileged system packages.
final File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app");
scanDirTracedLI(privilegedAppDir, mDefParseFlags
| PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR
| PackageParser.PARSE_IS_PRIVILEGED, scanFlags, 0);
// Collect ordinary system packages.
final File systemAppDir = new File(Environment.getRootDirectory(), "app");
scanDirTracedLI(systemAppDir, mDefParseFlags
| PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
...
// mAppInstallDir = new File(dataDir, "app");
scanDirTracedLI(mAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0);
上面的代码只是列出了部分目录的扫描。从源码中可知,当扫描以下目录时会设置 PARSE_IS_SYSTEM
。
/vender/overlay
/system/framework
/system/priv-app
/system/app
/vendor/app
/oem/app
因此,不管是安装应用还是扫描 /data/app
中的 APK,都不会设置 PARSE_IS_SYSTEM
,即普通应用是没有 persistent flag 权限的,即使在 AndroidManifest 中声明了也不会生效。
Android 9.0 的 parseBaseApplication()
方法中设置 FLAG_PERSISTENT
的那个 if
代码块没有检查 PARSE_IS_SYSTEM
flag(其实 Android 9.0 去掉了 PARSE_IS_SYSTEM
flag),而只要在 AndroidManifest 中声明了 persistent 属性就会设置 FLAG_PERSISTENT
。
/**
* Parse the {@code application} XML tree at the current parse location in a
* <em>base APK</em> manifest.
* <p>
* When adding new features, carefully consider if they should also be
* supported by split APKs.
*/
private boolean parseBaseApplication(Package owner, Resources res,
XmlResourceParser parser, int flags, String[] outError)
throws XmlPullParserException, IOException {
...
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;
}}
...
}