在安卓系统中,我该如何检查一个应用是否为非系统应用?

45
我正在获取一个ApplicationInfo对象列表,使用packageManager.getInstalledApplications(0),并尝试按系统应用和非系统应用对它们进行分类。
一段时间以来,我一直在使用here中描述的技术,但在我的应用程序中看到有些应用程序不在非系统应用程序列表中(例如Facebook,当可用时会要求系统将其安装在SD卡上)。接下来阅读ApplicationInfo.FLAG_SYSTEM的实际文档,并理解它实际上并没有过滤系统应用程序后,我现在正在寻找一种新方法。
我猜测可以通过获取系统和非系统应用程序的UID之间的差距来进行区分,但目前为止我没有找到答案。我也研究了其他标志,比如ApplicationInfo.FLAG_EXTERNAL_STORAGE,但我只支持API 1.5。
有人有解决方法吗(不涉及FLAG_SYSTEM)?
14个回答

34
PackageManager pm = mcontext.getPackageManager();
List<PackageInfo> list = pm.getInstalledPackages(0);

for(PackageInfo pi : list) {
    ApplicationInfo ai = pm.getApplicationInfo(pi.packageName, 0);

    System.out.println(">>>>>>packages is<<<<<<<<" + ai.publicSourceDir);

    if ((ai.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
        System.out.println(">>>>>>packages is system package"+pi.packageName);          
    }
}

@Ocool,我在我的问题中明确说明FLAG_SYSTEM实际上不起作用,所以这不是我要找的。 - Phil
@Phil 我知道FLAG_SYSTEM单独使用不起作用,这就是为什么要加上"&"条件,你可以试试,对我来说有效。 - Nitin
@Ocool,这是我使用了一段时间的方法,看起来它是有效的,直到你发现有些应用程序丢失了。事实上,FLAG_SYSTEM只会获取安装在设备系统镜像中的应用程序。这意味着它将无法获取安装在SD卡等位置的应用程序。获取所有应用程序的最佳方法是检查它们的目录("/data/apps")。 - Phil
1
@Phil SD卡上的应用程序始终是用户应用程序,对系统应用程序的更新也会进入/data/apps。 - sergio91pt

29

我原本以为系统镜像中的所有应用程序都是系统应用程序(通常安装在/ system / app中)。

如果FLAG_SYSTEM仅适用于系统应用程序,则即使是存储在外部存储中的应用程序也可以使用此方法:

boolean isUserApp(ApplicationInfo ai) {
    int mask = ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
    return (ai.flags & mask) == 0;
}

另一种选择是在您的手机中使用pm命令行程序。

语法:

pm list packages [-f] [-d] [-e] [-s] [-3] [-i] [-u] [--user USER_ID] [FILTER]

pm list packages: prints all packages, optionally only
  those whose package name contains the text in FILTER.  Options:
    -f: see their associated file.
    -d: filter to only show disbled packages.
    -e: filter to only show enabled packages.
    -s: filter to only show system packages.
    -3: filter to only show third party packages.
    -i: see the installer for the packages.
    -u: also include uninstalled packages.

代码:

ProcessBuilder builder = new ProcessBuilder("pm", "list", "packages", "-s");
Process process = builder.start();

InputStream in = process.getInputStream();
Scanner scanner = new Scanner(in);
Pattern pattern = Pattern.compile("^package:.+");
int skip = "package:".length();

Set<String> systemApps = new HashSet<String>();
while (scanner.hasNext(pattern)) {
    String pckg = scanner.next().substring(skip);
    systemApps.add(pckg);
}

scanner.close();
process.destroy();

然后:

boolean isUserApp(String pckg) {
    return !mSystemApps.contains(pckg);
}

21
您可以检查应用程序的签名是否与系统签名相匹配。如下所示:
/**
 * Match signature of application to identify that if it is signed by system
 * or not.
 * 
 * @param packageName
 *            package of application. Can not be blank.
 * @return <code>true</code> if application is signed by system certificate,
 *         otherwise <code>false</code>
 */
public boolean isSystemApp(String packageName) {
    try {
        // Get packageinfo for target application
        PackageInfo targetPkgInfo = mPackageManager.getPackageInfo(
                packageName, PackageManager.GET_SIGNATURES);
        // Get packageinfo for system package
        PackageInfo sys = mPackageManager.getPackageInfo(
                "android", PackageManager.GET_SIGNATURES);
        // Match both packageinfo for there signatures
        return (targetPkgInfo != null && targetPkgInfo.signatures != null && sys.signatures[0]
                .equals(targetPkgInfo.signatures[0]));
    } catch (PackageManager.NameNotFoundException e) {
        return false;
    }
}
你可以在我的博客上获取更多代码,如何检查应用程序是否为系统应用程序(通过签名)

@PankajKumar:我已经为iBall平板开发了一个系统应用程序,并按照您的博客使用了两种方法,我在两种情况下都获得了返回值“true”。(所以我希望我的应用程序真的已经成为了系统应用程序 :))。有两个疑问,(1)您的第二种方法“isAppPreLoaded”除了检查应用程序是否预装之外,是否也与“isSystemApp”执行相同的操作?;(2)预装的含义是什么,它是指检查应用程序是否驻留在system/app中,还是检查应用程序是否与ROM捆绑在一起,还是两者都包括? - Basher51
1
@Basher51 有时应用程序是预安装的,但它们不是系统应用程序。比如你开发了一个应用程序,用你的证书签名并添加到自定义AOSP代码中,由制造商证书签名。在这种情况下,你的应用程序是预安装的,但它不是系统应用程序(考虑证书)。因此,博客中的最后一种方法都会进行检查,首先检查应用程序是否预安装,如果是,则检查应用程序的签名。希望我现在清楚了 :) - Pankaj Kumar
2
@Basher51,您的应用程序是系统应用程序。在这种情况下,您构建一个应用程序并使用您的证书签名(与签署AOSP代码的证书不同)。到此为止,您的应用程序就像第三方应用程序一样。对吗?现在将该apk复制到system/app中。这一步不能使您的应用程序成为系统应用程序(即使它位于系统目录中,也有太多的API无法访问)。为了使应用程序成为系统应用程序,必须使用与您相同的证书进行签名,并且位置必须为/system/app。不用担心您的情况,您正在正确地操作。 - Pankaj Kumar
@sandy 不,它们不是系统应用程序。它们是来自谷歌的应用程序,并且使用谷歌的证书进行签名,而不是制造商的证书。即使这些应用程序安装在其他位置,也不会安装到/system/app目录下。现在我清楚了吗? - Pankaj Kumar
1
非常有趣 - Vlad
显示剩余6条评论

11

有两种非系统应用程序:

  1. 从Google Play商店下载的应用程序
  2. 设备制造商预装的应用程序

此代码将返回所有上述应用程序的列表:

ArrayList<ApplicationInfo> mAllApp = 
        mPackageManager.getInstalledApplications(PackageManager.GET_META_DATA);

for(int i = 0; i < mAllApp.size(); i++) {
    if((mAllApp.get(i).flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
         // 1. Applications downloaded from Google Play Store
        mAllApp1.add(mAllApp.get(i));
    }

    if((mAllApp.get(i).flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
        // 2. Applications preloaded in device by manufecturer
        mAllApp1.add(mAllApp.get(i));
    }
}

1
为什么你说从Play Store下载的应用都带有FLAG_SYSTEM标志?我可能错了,但根据我的个人经验,FLAG_UPDATED_SYSTEM_APP是指任何您更新过的系统应用。例如,当地图应用程序被安装并更新时,它会获取该标志。如果您卸载所有更新,则使用的标志可能是FLAG_SYSTEM或我在隐藏API中发现的另外2个标志:ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER(值为1 << 9)或PRIVATE_FLAG_SYSTEM_EXT(值为2097152)。不过我不知道它们具体是做什么的。 - Edw590

7
这里有一点误解。对于Android来说,“系统应用程序”的概念是指安装在系统镜像上的应用程序,它并没有说明它来自哪个开发者。因此,如果OEM决定将Facebook预装到系统镜像中,那么它就是一个系统应用程序,并且无论应用程序的更新安装在哪里,它都将继续保持这个身份。当然,它们不会被安装在系统镜像中,因为它是只读的。
所以,ApplicationInfo.FLAG_SYSTEM是正确的,但这似乎不是你要问的问题。我想你是在问一个包是否使用系统证书签名。这并不一定是什么好的指标,这可能因设备而异,即使在原生Android上,一些出人意料的组件也没有使用系统证书签名,尽管你可能希望它们这样做。
在较新版本的Android中,有一个新路径“/system/priv-app/”,试图成为“真正的”系统应用程序的安装位置。只是预装在系统映像中的应用程序最终会出现在“/system/app/”中。请参见AOSP Privileged vs System app

不错的观点。我创建了一个系统应用程序,它存储在system/app中。我已经使用系统密钥对其进行了签名(至少我希望如此)。但是,有没有一种方法(无论是以编程方式还是其他方式),完全确认我的应用程序现在已成为真正的系统应用程序(该应用程序与ROM中烘焙的密钥相同)? - Basher51

7

我认为这是一个不太完美的解决方案(如果/data/app在所有设备上都不是应用程序目录怎么办?),但经过全面搜索,这是我得出的结论:

for (ApplicationInfo ai : appInfo) {
    if (ai.sourceDir.startsWith("/data/app/")) {
        //Non-system app
    }
    else {
        //System app
    }
}

17
如果您更新系统应用程序,它将把更新放入/data/app目录中。但它仍然是一个系统应用程序。 - Ion Aalbers
@IonAalbers:我也有这个疑问。大多数地方都写着系统应用程序位于system/app,用户应用程序位于data/app。为什么当system/app更新时,它的apk文件会存储在data/app中?能否请您解释得更详细一些呢? - Basher51
1
@Basher51,位于/system/app/位置的应用程序并不意味着它是系统应用程序。它只是预装的应用程序。至于更新应用程序的疑问,我可以猜测他们编写了这个逻辑是考虑到“恢复出厂设置”的情况。如果应用程序在同一位置更新,那么在恢复出厂设置时将很难找到原始应用程序状态。 - Pankaj Kumar
完全不工作。第三方应用程序也有以“/data/app/”开头的目录。 - Vlad
1
关于Ion所说的,APK仍然留在系统文件夹中(它留在两个文件夹中)。在这种情况下,应首先检查应用程序是否在系统文件夹中(如/system/app或/system/priv-app),然后再检查/data/app。这样可以使答案得以实现,但逻辑会被倒置。 - Edw590

7
如果一个应用程序不是系统应用程序,它必须有一个启动意图,通过这个意图可以启动该应用程序。如果启动意图为null,则它是一个系统应用程序。
系统应用程序的示例包括:“com.android.browser.provider”,“com.google.android.voicesearch”。
对于上述应用程序,当您查询启动意图时,将会得到NULL。
PackageManager pm = getPackageManager();
List<ApplicationInfo> packages = pm.getInstalledApplications(PackageManager.GET_META_DATA);
for(ApplicationInfo packageInfo:packages){
    if( pm.getLaunchIntentForPackage(packageInfo.packageName) != null ){
                String currAppName = pm.getApplicationLabel(packageInfo).toString();
               //This app is a non-system app
    }
}

1
这真是太聪明了。这是一种找到可启动应用程序的好方法。 - Chris Feist
7
许多系统应用都具有启动意图,例如com.android.settings。 - Alex Cohn
1
非常好的回答 - 到现在为止,我还想不出可启动的应用程序如何被视为系统应用程序。 - Björn Hallström
迄今为止,这是有史以来最好的答案,并且在这么多年后仍然运行良好。@alex-cohn 是的,有许多系统应用程序具有启动意图,但这也适用于仅列出具有启动意图的应用程序,具体取决于开发人员的项目。 - Yosidroid

5
这是其他回答的简化和更高效的版本。如果您直接遍历ApplicationInfos,它会更加高效。
    List<ApplicationInfo> applications = context.getPackageManager()
            .getInstalledApplications(PackageManager.GET_META_DATA);
    for(ApplicationInfo appInfo : applications){
        if((appInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0){
            // Not a system app
        }
    }

4

以下是通过包名判断应用程序是否为系统应用的不同可能方式(使用了本帖中的一些代码)

package com.test.util;

import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;

import java.io.IOException;
import java.io.InputStream;
import java.util.HashSet;
import java.util.Scanner;
import java.util.Set;
import java.util.regex.Pattern;

import timber.log.Timber;


public class SystemAppChecker {
    private PackageManager packageManager = null;

    public SystemAppChecker(Context context) {
        packageManager = context.getPackageManager();
    }

    /**
     * Check if system app by 'pm' command-line program
     *
     * @param packageName
     *            package name of application. Cannot be null.
     * @return <code>true</code> if package is a system app.
     */
    public boolean isSystemAppByPM(String packageName) {
        if (packageName == null) {
            throw new IllegalArgumentException("Package name cannot be null");
        }
        ProcessBuilder builder = new ProcessBuilder("pm", "list", "packages", "-s");
        Process process = null;
        try {
            process = builder.start();
        } catch (IOException e) {
            Timber.e(e);
            return false;
        }

        InputStream in = process.getInputStream();
        Scanner scanner = new Scanner(in);
        Pattern pattern = Pattern.compile("^package:.+");
        int skip = "package:".length();

        Set<String> systemApps = new HashSet<String>();
        while (scanner.hasNext(pattern)) {
            String pckg = scanner.next().substring(skip);
            systemApps.add(pckg);
        }

        scanner.close();
        process.destroy();

        if (systemApps.contains(packageName)) {
            return true;
        }
        return false;
    }

    /**
     * Check if application is preloaded.
     *
     * @param packageName
     *            package name of application. Cannot be null.
     * @return <code>true</code> if package is preloaded.
     */
    public boolean isSystemPreloaded(String packageName) {
        if (packageName == null) {
            throw new IllegalArgumentException("Package name cannot be null");
        }
        try {
            ApplicationInfo ai = packageManager.getApplicationInfo(
                    packageName, 0);
            if (ai.sourceDir.startsWith("/system/app/") || ai.sourceDir.startsWith("/system/priv-app/")) {
                return true;
            }
        } catch (NameNotFoundException e) {
            Timber.e(e);
        }
        return false;
    }

    /**
     * Check if the app is system signed or not
     *
     * @param packageName
     *            package of application. Cannot be blank.
     * @return <code>true</code> if application is signed by system certificate,
     *         otherwise <code>false</code>
     */
    public boolean isSystemSigned(String packageName) {
        if (packageName == null) {
            throw new IllegalArgumentException("Package name cannot be null");
        }
        try {
            // Get packageinfo for target application
            PackageInfo targetPkgInfo = packageManager.getPackageInfo(
                    packageName, PackageManager.GET_SIGNATURES);
            // Get packageinfo for system package
            PackageInfo sys = packageManager.getPackageInfo(
                    "android", PackageManager.GET_SIGNATURES);
            // Match both packageinfo for there signatures
            return (targetPkgInfo != null && targetPkgInfo.signatures != null && sys.signatures[0]
                    .equals(targetPkgInfo.signatures[0]));
        } catch (PackageManager.NameNotFoundException e) {
            Timber.e(e);
        }
        return false;
    }

    /**
     * Check if application is installed in the device's system image
     *
     * @param packageName
     *            package name of application. Cannot be null.
     * @return <code>true</code> if package is a system app.
     */
    public boolean isSystemAppByFLAG(String packageName) {
        if (packageName == null) {
            throw new IllegalArgumentException("Package name cannot be null");
        }
        try {
            ApplicationInfo ai = packageManager.getApplicationInfo(
                    packageName, 0);
            // Check if FLAG_SYSTEM or FLAG_UPDATED_SYSTEM_APP are set.
            if (ai != null
                    && (ai.flags & (ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)) != 0) {
                return true;
            }
        } catch (NameNotFoundException e) {
            Timber.e(e);
        }
        return false;
    }
}

一个应用程序如何被标记为“isSystemSigned”?我需要系统密钥来完成吗? - lucky1928
"isSystemSigned" 意味着使用平台密钥对您的 apk 进行签名。请参考此链接以创建开发构建 - https://github.com/aosp-mirror/platform_build/tree/master/target/product/security - Vinayak Bevinakatti
1
如果您有更多问题,那么这些帖子可能会回答其中一些 - https://dev59.com/l1oU5IYBdhLWcg3wTVzE#46729285 和 https://dev59.com/m3A65IYBdhLWcg3wyRyk#3651653 - Vinayak Bevinakatti

2

所以,我想在这里放一个实用类,它是我根据这个线程和其他一些线程的知识制作的。但在继续之前,我要解释一些术语,如果我都理解正确的话,这些术语是从那个类中复制出来的,并且在其中使用。

在KitKat 4.4以下版本,/system/app中的所有应用程序都被赋予特权权限。即使是计算器应用程序也有这些权限。这可能会造成安全漏洞。因此,它们被分为普通和特权系统应用程序,普通应用程序在KitKat 4.4以上没有特权权限。因此,这些实用程序考虑到了这一点。他们还考虑到以下指定:
- 平台签名应用程序:使用平台/系统密钥签名的任何应用程序(因此它们具有系统签名权限),无论是否安装在系统分区中。 - 系统应用程序:安装在系统分区中的任何应用程序。 - 更新的系统应用程序:任何已更新的系统应用程序(意味着现在它也安装在/data/app中)。 - 特权系统应用程序:在KitKat 4.4以下,安装在/system/app中的任何应用程序;从KitKat 4.4开始,仅安装在/system/priv-app中的应用程序(我真正的意思是仅/system)。这些应用程序具有特权权限。 - 普通系统应用程序:仅在KitKat 4.4及以上版本中,这些应用程序没有特权权限,尽管它们仍然是系统应用程序。在KitKat 4.4以下,它们不存在。
系统分区说明:直到Oreo 8.1,只有一个/system。从Pie (9)开始,还有/vendor和/product。
所以,记住这一点,这里有两个函数:
/**
 * <p>Checks if an app is installed on the system partitions and was updated.</p>
 *
 * @param applicationInfo an instance of {@link ApplicationInfo} for the package to be checked
 *
 * @return true if it is, false otherwise
 */
private static boolean isUpdatedSystemApp(@NonNull final ApplicationInfo applicationInfo) {
    return (applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
}

/**
 * <p>Checks if an app is installed in the system partitions (ordinary app or privileged app, doesn't matter).</p>
 *
 * @param applicationInfo an instance of {@link ApplicationInfo} for the package to be checked
 *
 * @return true if it is, false otherwise
 */
private static boolean isSystemApp(@NonNull final ApplicationInfo applicationInfo) {
    // Below Android Pie (9), all system apps were in /system. As of Pie, they can ALSO be in /vendor and /product.
    boolean ret_value = (applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
        // FLAG_SYSTEM checks if it's on the system image, which means /system. So to check for /vendor and
        // /product, here are 2 special flags.
        ret_value = ret_value || (applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0;
        ret_value = ret_value || (applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRODUCT) != 0;
    }

    return ret_value;
}

为了检查一个应用程序是否是特权系统应用程序或普通系统应用程序,和/或是否使用平台/系统密钥签名,我将在下面留下3个函数。我相信这与问题无关,但我会放在这里以防像我这样的人需要它。
/**
 * <p>Checks if an app is an ordinary system app (installed on the system partitions, but no privileged or signature
 * permissions granted to it).</p>
 * <p>Note: will return false for any app on KitKat 4.4 and below.</p>
 *
 * @param applicationInfo an instance of {@link ApplicationInfo} for the package to be checked
 *
 * @return true if it is, false otherwise
 */
private static boolean isOrdinarySystemApp(@NonNull final ApplicationInfo applicationInfo) {
    // It's an ordinary system app if it doesn't have any special permission privileges (it's not a Privileged app
    // nor is it signed with the system key).
    boolean ret_value = isSystemApp(applicationInfo) && !hasPrivilegedPermissions(applicationInfo);
    final boolean signed_system_key = hasSystemSignaturePermissions(applicationInfo);
    ret_value = ret_value && signed_system_key;

    return ret_value;
}

/**
 * <p>Checks if an app has signature permissions - checks if it's signed with the platform/system certificate by
 * comparing it to the "android" package.</p>
 * <br>
 * <p>ATTENTION: if the chosen app was signed multiple times and the system is running below Android Pie, this check
 * may return false wrongly, since it checks if ALL the signatures from the "android" package and the chosen
 * application match. If at least one doesn't match in both, this will return false. So use with caution in case of
 * multiple signers. With only one signer, it's all right.</p>
 *
 * @param applicationInfo an instance of {@link ApplicationInfo} for the package to be checked
 * @return true if it is, false otherwise
 */
private static boolean hasSystemSignaturePermissions(@NonNull final ApplicationInfo applicationInfo) {
    // If on Pie or above, check with a private flag (appeared on Pie only).
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
        return (applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_SIGNED_WITH_PLATFORM_KEY) != 0;
    }

    // Else, check by comparing signatures of a platform-signed app and the chosen app.
    return UtilsGeneral.getContext().getPackageManager().checkSignatures(applicationInfo.packageName, "android")
            == PackageManager.SIGNATURE_MATCH;
}

/**
 * <p>"Value for {@link ApplicationInfo#flags}: set to {@code true} if the application
 * is permitted to hold privileged permissions.</p>
 *
 * {@hide}"
 * <p>NOTE: Only on API 19 through API 22.</p>
 */
private static final int FLAG_PRIVILEGED = 1 << 30;

/**
 * <p>Checks if an app is a Privileged App.</p>
 * <p>Note: will return true for any system app below KitKat 4.4.</p>
 *
 * @param applicationInfo an instance of {@link ApplicationInfo} for the package to be checked
 *
 * @return true if it is, false otherwise
 */
private static boolean hasPrivilegedPermissions(@NonNull final ApplicationInfo applicationInfo) {
    // Check if it's an app installed in the system partitions. If it is, check with methods that apply only to
    // apps installed on the system partitions.
    if (isSystemApp(applicationInfo)) {
        // If it's below KitKat 4.4 and it's a system app, it's a privileged one automatically.
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
            return true;
        }

        // If on Marshmallow or above, check with a private flag.
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if ((applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {
                return true;
            }
        }

        // If between KitKat 4.4 and Lollipop 5.1, use a deleted flag.
        if ((applicationInfo.flags & FLAG_PRIVILEGED) != 0) {
            return true;
        }
    }

    // In case none returned true above, the app may still be signed with the platform/system's key, which will
    // grant it exactly all permissions there are (which includes privileged permissions - ALL permissions).
    return hasSystemSignaturePermissions(applicationInfo);
}

如果您愿意,可以将此最后一个与上面的内容合并,但我并不推荐这样做。只有在系统应用程序未更新时才能正常工作。

/**
 * <p>Gets a list of folders a system app might be installed in, depending on the device's Android version.</p>
 * <p>Note that an updated system app will report as being installed in /data/app. For these locations to be
 * checked, the app must not have been updated. If it has, it's not possible to tell using the directory, I think.</p>
 *
 * @param privileged_app true if it's to return a list for privileged apps, false if it's for ordinary system apps,
 *                       null if it's to return a list for both types
 *
 * @return a list of folders its APK might be in
 */
@NonNull
private static String[] getAppPossibleFolders(@Nullable final Boolean privileged_app) {
    final Collection<String> ret_folders = new ArrayList<>(5);

    final String PRIV_APP_FOLDER = "/system/priv-app";
    final String ORD_APP_SYSTEM_FOLDER = "/system/app";
    final String ORD_APP_VENDOR_FOLDER = "/vendor/app";
    final String ORD_APP_PRODUCT_FOLDER = "/product/app";

    if (privileged_app == null) {
        ret_folders.add(PRIV_APP_FOLDER);
        ret_folders.add(ORD_APP_SYSTEM_FOLDER);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
            ret_folders.add(ORD_APP_VENDOR_FOLDER);
            ret_folders.add(ORD_APP_PRODUCT_FOLDER);
        }
    } else if (privileged_app) {
        ret_folders.add(PRIV_APP_FOLDER);
    } else {
        ret_folders.add(ORD_APP_SYSTEM_FOLDER);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
            ret_folders.add(ORD_APP_VENDOR_FOLDER);
            ret_folders.add(ORD_APP_PRODUCT_FOLDER);
        }
    }

    // Leave it in 0 size allocation. Or null values will appear, and I don't want to need to be careful about it.
    return ret_folders.toArray(new String[0]);

    /*
    Use with:

    // If it's an updated system app, its APK will be said to be in /data/app, and the one on the system partitions
    // will become unused. But if it's not updated, it's all fine and the APK path can be used to check if it's
    // a privileged app or not.
    if (!isUpdatedSystemApp(applicationInfo)) {
        for (final String folder : getAppPossibleFolders(false)) {
            if (applicationInfo.sourceDir.startsWith(folder)) {
                return true;
            }
        }
    }
    */
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接