0. Persistent flag 介绍

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 属性。

1. 应用的 Persistent flag 权限

不是说只要在 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 来分析源码。

1.1 Android 8.1

对于 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 中声明了也不会生效。

1.2 Android 9.0

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;
    }}
    ...
}