1 哪些app属于system app?

为了区分privilege app和system app,这里先说明system app是什么,避免之后的讨论概念混乱。

在PackageManagerService中对是否是system app的判断:
具有ApplicationInfo.FLAG_SYSTEM标记的,被视为System app。

frameworks\base\services\core\java\com\android\server\pm\PackageManagerService.java

final boolean isSystem = isSystemApp(pkgSetting) || isUpdatedSystemApp(pkgSetting);
    private static boolean isSystemApp(PackageSetting ps) {
        return (ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0;
    }

    private static boolean isUpdatedSystemApp(PackageSetting ps) {
        return (ps.pkgFlags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
    }

有两类app属于System app:

1.1 第一类System app: 特定的shared uid的app 属于system app
例如:shared uid为android.uid.system,android.uid.phone,android.uid.log,android.uid.nfc,android.uid.bluetooth,android.uid.shell。这类app都被赋予了ApplicationInfo.FLAG_SYSTEM标志。在PackageManagerService的构造方法中,代码如下:

mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
        mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
        mSettings.addSharedUserLPw("android.uid.log", LOG_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
        mSettings.addSharedUserLPw("android.uid.nfc", NFC_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
        mSettings.addSharedUserLPw("android.uid.bluetooth", BLUETOOTH_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
        mSettings.addSharedUserLPw("android.uid.shell", SHELL_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
        mSettings.addSharedUserLPw("android.uid.se", SE_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
        mSettings.addSharedUserLPw("android.uid.networkstack", NETWORKSTACK_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);

1.2 第二类System app: 特定目录中的app属于system app
特定目录包括:/vendor/overlay,/system/framework,/system/priv-app,/system/app,/vendor/app,/oem/app。这些目录中的app,被视为system app。

在PackageManagerService的构造方法中,代码如下:

// /vendor/overlay folder
File vendorOverlayDir = new File(VENDOR_OVERLAY_DIR);
scanDirLI(vendorOverlayDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags | SCAN_TRUSTED_OVERLAY, 0);

// Find base frameworks (resource packages without code). /system/framework folder
File frameworkDir = new File(Environment.getRootDirectory(), "framework");
scanDirLI(frameworkDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR
| PackageParser.PARSE_IS_PRIVILEGED,
scanFlags | SCAN_NO_DEX, 0);

// Collected privileged system packages. /system/priv-app folder
final File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app");
scanDirLI(privilegedAppDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR
| PackageParser.PARSE_IS_PRIVILEGED, scanFlags, 0);

// Collect ordinary system packages. /system/app folder
final File systemAppDir = new File(Environment.getRootDirectory(), "app");
scanDirLI(systemAppDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);

// Collect all vendor packages.
File vendorAppDir = new File("/vendor/app");
scanDirLI(vendorAppDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);

// Collect all OEM packages. /oem/app folder
final File oemAppDir = new File(Environment.getOemDirectory(), "app");
scanDirLI(oemAppDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);

2 什么是privileged app(特权app)

注:privileged app,在本文中称之为 特权app,主要原因是此类特权app可以使用protectionLevel为signatureOrSystem或者protectionLevel为signature|privileged的权限。

从PackageManagerService的isPrivilegedApp()可以看出特权app是具有ApplicationInfo.PRIVATE_FLAG_PRIVILEGED标志的一类app。

resolveInfo.activityInfo.applicationInfo.isPrivilegedApp()
frameworks/base/core/java/android/content/pm/ApplicationInfo.java
public boolean isPrivilegedApp() {
        return (privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
    }

特权app首先必须是System app。也就是说 System app分为普通的system app和特权的system app。

直观的(但不准确严谨)说,普通的system app就是/system/app目录中的app,特权的system app就是/system/priv-app目录中的app。
区分普通system app和特权app的目的是澄清这个概念:"signatureOrSystem"权限中的System不是为普通system app提供的,而是只有特权app能够使用。

所以,当我们说起system app时,通常指具有特定uid和特定目录中的app,包含了普通的system app和特权app。当我们说起有访问System权限或者privileged权限的app时,通常指特权app。

3 哪些app属于privileged app(特权app)?
特权app首先是System app,然后要具有ApplicationInfo.PRIVATE_FLAG_PRIVILEGED标志。
有两类app属于privileged app(特权app):

参考PackageManagerService的构造方法。

3.1 第一类privileged app: 特定uid的app
shared uid为"android.uid.system","android.uid.phone","android.uid.log","android.uid.nfc","android.uid.bluetooth","android.uid.shell"的app被赋予了privileged的权限。这些app同时也是system app。

代码如下:

mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
        mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
        mSettings.addSharedUserLPw("android.uid.log", LOG_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
        mSettings.addSharedUserLPw("android.uid.nfc", NFC_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
        mSettings.addSharedUserLPw("android.uid.bluetooth", BLUETOOTH_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
        mSettings.addSharedUserLPw("android.uid.shell", SHELL_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
        mSettings.addSharedUserLPw("android.uid.se", SE_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
        mSettings.addSharedUserLPw("android.uid.networkstack", NETWORKSTACK_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);

3.2 第二类privileged app:/system/framework和/system/priv-app目录下的app
/system/framework和/system/priv-app目录下的app被赋予了privileged的权限。
其中/system/framework目录中的apk,只是包含资源,不包含代码(dex)。

/ PackageManagerService的构造方法
// /system/framework
File frameworkDir = new File(Environment.getRootDirectory(), "framework");
scanDirLI(frameworkDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR
| PackageParser.PARSE_IS_PRIVILEGED,
scanFlags | SCAN_NO_DEX, 0);

// /system/priv-app
final File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app");
scanDirLI(privilegedAppDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR
| PackageParser.PARSE_IS_PRIVILEGED, scanFlags, 0);

PackageParser.PARSE_IS_PRIVILEGED标志最终会转换为Package的ApplicationInfo.PRIVATE_FLAG_PRIVILEGED标志。大概的代码流程,如下:

scanDirLI(PackageParser.PARSE_IS_PRIVILEGED)

-> scanPackageLI(file, parseFlags | PackageParser.PARSE_MUST_BE_APK, scanFlags, currentTime, null);

-> PackageParser.Package scannedPkg = scanPackageLI(pkg, parseFlags, scanFlags | SCAN_UPDATE_SIGNATURE, currentTime, user);

-> final PackageParser.Package res = scanPackageDirtyLI(pkg, parseFlags, scanFlags, currentTime, user);

-> scanPackageDirtyLI()中
if ((parseFlags & PackageParser.PARSE_IS_PRIVILEGED) != 0) {
pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
}

4.1 验证某个app是否获得了某个权限的方法
可以用下面2个命令来验证:

adb shell dumpsys package permission <权限名>

adb shell dumpsys package <包名>
 
其中,adb shell dumpsys package permission <权限名>可以查看某个权限是谁声明的,谁使用了。
adb shell dumpsys package <包名>可以看到某个app是否获得了某个权限。

哪些app可以使用某app提供的signature权限或signatureOrSystem权限?
如果App A声明了signatureOrSystem权限,即android:protectionLevel="signature|privileged"或者android:protectionLevel="signatureOrSystem",则可以使用该权限的app包括:

(1)与App A有相同签名的app
(2)与系统签名相同的app,即与厂商签名(厂商ROM中的系统app签名)相同的app
(3)在/system/priv-app目录中的app,不管是否满足(1)和(2)的条件。即任意app只要放到了/system/priv-app就可以使用App A的signatureOrSystem级别的权限。
如果App A声明了signature权限,即android:protectionLevel="signature",则可以使用该权限的app包括:

(1)与App A有相同签名的app
(2)与系统签名相同的app,即与厂商签名(厂商ROM中的系统app签名)相同的app
注意:与系统签名相同,即与厂商签名相同,这是指厂商推出的app,这些app有很大的访问权限。