如何检查应用程序是不是是 Android 中的非系统应用程序?

Posted

技术标签:

【中文标题】如何检查应用程序是不是是 Android 中的非系统应用程序?【英文标题】:How do I check if an app is a non-system app in Android?如何检查应用程序是否是 Android 中的非系统应用程序? 【发布时间】:2012-02-05 18:09:43 【问题描述】:

我正在获取带有packageManager.getInstalledApplications(0) 的ApplicationInfo 对象列表,并尝试根据它们是否为系统应用程序对其进行分类。

有一段时间我一直在使用here 描述的技术,但是在我的应用程序中看到之后,一些应用程序不在非系统应用程序列表中(例如 Facebook,可用时要求系统将自身安装在 SD 卡上)。在接下来阅读ApplicationInfo.FLAG_SYSTEM 的实际文档并了解它实际上并没有过滤系统应用程序之后,我现在正在寻找一种新方法。

我的猜测是系统应用程序的 UID 和非系统应用程序的 UID 之间存在很大差距,我可以收集这些应用程序来进行区分,但到目前为止我还没有找到答案。我还研究了其他标志,例如 ApplicationInfo.FLAG_EXTERNAL_STORAGE,但我支持 API 1.5。

有没有人对此有真正的解决方案(不涉及FLAG_SYSTEM)?

【问题讨论】:

【参考方案1】:
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 我知道 FLAG_SYSTEM 不能单独工作,为什么“&”条件,试试吧,它对我有用。 @Ocool,这是我一直在使用的,并且它似乎可以工作,直到您注意到某些应用程序丢失为止。事实上FLAG_SYSTEM 只是获取installed in the device's system image 的应用程序。这意味着它不会在 SD 卡等上安装应用程序。不幸的是,获取所有应用程序的最佳方法是检查它们的目录(“/data/apps”)。 @Phil SD 卡上的应用程序始终是用户应用程序,系统应用程序的更新也转到 /data/apps【参考方案2】:

我的印象是系统映像中的所有应用程序都是系统应用程序(通常安装在/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);

【讨论】:

【参考方案3】:

您可以检查它与系统签署的应用程序的签名。如下所示

/**
 * 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;
    


您可以在我的博客How to check if application is system app or not (By signed signature)获取更多代码

【讨论】:

我还没有机会尝试这个,但如果它有效的话,我真的很喜欢这种方法 - 绝对是进行比较的好方法。 @Phil 很高兴知道它对您的期望有效。谢谢。 @Basher51 有时会预装应用程序,但这些不是系统应用程序。就像您开发了一个应用程序,使用您的证书签名并添加到由制造商证书签名的自定义 AOSP 代码。在这种情况下,您的应用程序是预安装的,但它不是系统应用程序(考虑到证书)。所以博客上的最后一个方法两者都做,首先检查是否预安装了应用程序,如果是,则检查应用程序的签名。希望我现在清楚:)\ @Basher51 您的应用程序是系统应用程序。在这种情况下,您构建了一个应用程序并由您的证书签名(与签署 AOSP 代码的证书不同)。到目前为止,您的应用程序就像第三方应用程序。正确的?现在您将该 apk 复制到 system/app。此步骤不会使您的应用程序成为系统应用程序(您的应用程序无法访问的 API 太多,即使在系统目录中也是如此)。为此,它必须由与您正在执行的相同证书签名,并且位置必须是 /system/app。不用担心您的情况,您的方法是正确的。 非常有趣【参考方案4】:

好吧,我认为这是一个草率的解决方案(如果 /data/app 不是所有设备上的应用程序目录怎么办?),但经过彻底搜索,这就是我来最多:

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

【讨论】:

如果你更新一个系统应用,它会将更新放到/data/app目录下。但它仍然是一个系统应用程序。 @IonAalbers:我也有这个疑问。大多数地方都写到系统应用程序驻留在系统/应用程序中,用户应用程序驻留在数据/应用程序中。为什么当系统/应用程序更新时它的 apk存储在数据/应用程序中?你能解释一下吗? @Basher51 位置为/system/app/ 的应用程序并不意味着它是系统应用程序。这意味着应用程序已预加载。对于更新应用程序的疑问,我可以猜到,他们编写了这个逻辑来记住factory-reset。如果应用程序将在同一位置更新,则在恢复出厂设置时将很难找到原始应用程序状态。 根本不醒。 3rd 方应用的目录也以/data/app/ 开头 关于 Ion 所说的,APK 仍然保留在系统文件夹中(它保留在两个文件夹中)。在这种情况下,应首先检查应用程序是否在系统文件夹中(如 /system/app 或 /system/priv-app),然后才在 /data/app 中。这样可以使这个答案起作用,但逻辑​​相反。【参考方案5】:

这里有点误会。对于 Android,“系统应用”的概念是安装在系统映像上的,它没有说明它来自哪个开发者。因此,如果 OEM 决定将 Facebook 预加载到系统映像中,那么它就是一个系统应用程序,并且将继续如此,无论应用程序的更新安装在何处。它们肯定不会安装在系统映像上,因为它是只读的。

所以 ApplicationInfo.FLAG_SYSTEM 是正确的,但这似乎不是您要问的问题。我想你是在问一个包是否用系统证书签名。这不一定是任何事情的良好指标,这可能因设备而异,并且香草 Android 上的一些令人惊讶的组件未使用系统证书签名,即使您可能期望它们是这样。

在较新版本的 Android 中,有一个新路径 /system/priv-app/ 尝试成为“真实”系统应用的安装位置。刚刚预加载到系统映像上的应用程序最终会出现在 /system/app/ 中。见AOSP Privileged vs System app

【讨论】:

很好的视角。我创建了一个系统应用程序,它驻留在系统/应用程序中。我确实使用系统密钥对其进行了签名(至少这是我希望的)。但是有没有办法(以编程方式或否则)要完全确认我的应用程序现在已成为真正的系统应用程序(使用与 ROM 中烘焙的相同密钥签名)?【参考方案6】:

有两种类型的非系统应用程序:

    从 Google Play 商店下载的应用程序 设备制造商预加载的应用程序

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

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));
    

【讨论】:

为什么说从 Play 商店下载的应用带有 FLAG_SYSTEM?我可能是错的,但根据我的个人经验,FLAG_UPDATED_SYSTEM_APP 是您更新的任何系统应用程序。就像,当地图安装并且你更新它时,它会得到那个标志。如果您卸载所有更新,使用的标志可能是 FLAG_SYSTEM 或我在隐藏 API 上找到的其他 2 个标志之一:ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER(1 &lt;&lt; 9 的值)或 PRIVATE_FLAG_SYSTEM_EXT(2097152 的值)。不知道他们在做什么。【参考方案7】:

如果一个应用程序是一个非系统应用程序,它必须有一个启动意图,通过它可以启动它。如果启动意图为空,则它是一个系统应用程序。

系统应用示例:“com.android.browser.provider”、“com.google.android.voicesearch”。

对于上述应用,当您查询启动 Intent 时,您将获得 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
    

【讨论】:

这是天才。这是查找可启动应用程序的好方法。 许多系统应用都有启动意图,例如com.android.settings 很好的答案 - 到目前为止,我无法弄清楚可启动应用程序如何被视为系统应用程序【参考方案8】:

这是此处列出的其他响应的简化且更有效的版本。如果直接遍历 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
        
    

【讨论】:

【参考方案9】:

这里有不同的方法可以通过包名来判断应用是否是系统应用(使用了这篇文章中的一些代码)

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”?我需要系统密钥吗? "isSystemSigned" 表示使用平台密钥签署您的 apk。请参阅此内容以创建开发版本 - github.com/aosp-mirror/platform_build/tree/master/target/… 如果您还有更多问题,那么这些帖子可能会回答其中一些问题 - ***.com/questions/37586255/… 和 ***.com/a/3651653/28557【参考方案10】:
if (!packageInfo.sourceDir.toLowerCase().startsWith("/system/"))

【讨论】:

这不一定是真的,你总是可以用平台密钥签署一个应用程序(假设你有权访问)并使用系统 sharedUserId 安装它,它会作为系统应用【参考方案11】:

如果有一个 APK 文件并想检查它是系统应用还是用户安装 一个简单的逻辑:- 系统应用文件不可写

private boolean isSystemApkFile(File file)
   return !file.canWrite();

【讨论】:

非系统APK文件也不可写,因为它们属于另一个用户。【参考方案12】:

所以我想在这里放一个我用这个线程和其他一些知识制作的实用程序类。但在我继续之前,对一些术语的解释,如果我没听错的话,从那个类中复制过来,用在上面。

在 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 以下, 它们不存在。

系统分区说明:在奥利奥8.1之前,有 只有一个:/系统。从 Pie (9) 开始,还有 /vendor 和 /产品。

因此,考虑到这一点,这里有 2 个函数:

/**
 * <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;
            
        
    
    */

【讨论】:

【参考方案13】:

这是我为此目的编写的 AppUtil。 用法示例:

new AppsUtil(this).printInstalledAppPackages(AppsUtil.AppType.USER);

AppsUtil.java

import java.util.ArrayList;
import java.util.List;

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 android.util.Log;

public class AppsUtil

    public static final String  TAG = "PackagesInfo";
    private Context             _context;
    private ArrayList<PckgInfo> _PckgInfoList;

    public enum AppType 
    
        ALL 
            @Override
            public String toString() 
              return "ALL";
            
          ,
        USER 
            @Override
            public String toString() 
              return "USER";
            
          ,
        SYSTEM 
            @Override
            public String toString() 
              return "SYSTEM";
            
          
    

    class PckgInfo
    
        private AppType appType;
        private String  appName     = "";
        private String  packageName = "";
        private String  versionName = "";
        private int     versionCode = 0;

        private void prettyPrint()
        
            Log.i(TAG, appName + "\n  AppType: " + appType.toString() + "\n  Package: " + packageName + "\n  VersionName: " + versionName + "\n  VersionCode: " + versionCode);
        
    

    public AppsUtil(Context context)
    
        super();
        this._context = context;
        this._PckgInfoList = new ArrayList<PckgInfo>();
    

    public void printInstalledAppPackages(AppType appType)
    
        retrieveInstalledAppsPackages();
        Log.i(TAG, "");
        for (int i = 0; i < _PckgInfoList.size(); i++)
        
            if (AppType.ALL == appType)
            
                _PckgInfoList.get(i).prettyPrint();
            
            else
            
                if (_PckgInfoList.get(i).appType == appType)
                    _PckgInfoList.get(i).prettyPrint();
            
        
    

    public ArrayList<PckgInfo> getInstalledAppPackages(AppType appType)
    
        retrieveInstalledAppsPackages();
        ArrayList<PckgInfo> resultPInfoList = new ArrayList<PckgInfo>();

        if (AppType.ALL == appType)
        
            return _PckgInfoList;
        
        else
        
            for (int i = 0; i < _PckgInfoList.size(); i++)
            
                if (_PckgInfoList.get(i).appType == appType)
                    resultPInfoList.add(_PckgInfoList.get(i));
            
            return resultPInfoList;
        
    

    private void retrieveInstalledAppsPackages()
    
        PackageManager pm = _context.getPackageManager();
        List<PackageInfo> packs = pm.getInstalledPackages(0);
        for (PackageInfo pi : packs)
        
            try
            
                PckgInfo newInfo = new PckgInfo();
                ApplicationInfo ai = pm.getApplicationInfo(pi.packageName, 0);

                newInfo.appType = getAppType(ai);
                newInfo.appName = pi.applicationInfo.loadLabel(pm).toString();
                newInfo.packageName = pi.packageName;
                newInfo.versionName = pi.versionName;
                newInfo.versionCode = pi.versionCode;
                _PckgInfoList.add(newInfo);
            
            catch (NameNotFoundException e)
            
                e.printStackTrace();
            
        
    

    AppType getAppType(ApplicationInfo ai)
    
        AppType resultType ;
        if (isUserApp(ai))
            resultType = AppType.USER;
        else
            resultType = AppType.SYSTEM;

        return resultType;
    

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

【讨论】:

【参考方案14】:

您可以使用checkSignatures 来确定应用是否为系统应用。

所有系统应用都使用相同的密钥进行签名。

https://developer.android.com/reference/android/content/pm/PackageManager#checkSignatures(java.lang.String,%20java.lang.String)

用系统密钥签名的是“android”包。

    val checkPackage: String = "com.package.to.check"
    val systemPackageName = "android"
    if (packageManager.checkSignatures(systemPackageName, checkPackage) == PackageManager.SIGNATURE_MATCH) 
        Log.d("TUT", "System app")
     else 
        Log.d("TUT", "Non-System app")
    

【讨论】:

在 KitKat 及以下版本中,我不确定我是否信任 checkSignatures()。如果我理解正确,它可能在那些 Android 版本上被错误地实现。在这种情况下,最好不要检查所有签名,或者使用GET_SIGNATURES 实现一些信任链以了解所有证书是否确实有效。

以上是关于如何检查应用程序是不是是 Android 中的非系统应用程序?的主要内容,如果未能解决你的问题,请参考以下文章

如何检查字符串是不是是android中的有效电子邮件? [复制]

如何检查由单独 Android 应用程序中的服务启动的 Android 应用程序是不是已完成加载?

如何检查应用程序是不是在 Android 上运行?

我如何检查用户是不是真的在他的手机上启用了 android 中的 gps 位置

如何在android中检查3g是不是处于活动状态

如何在android中检查网络是不是连接到wifi [重复]