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

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

1
如果有一个APK文件,并且想要检查它是系统应用还是用户安装的应用,一个简单的逻辑: 系统应用文件不可写
private boolean isSystemApkFile(File file){
   return !file.canWrite();
}

1
非系统APK文件也不可写,因为它们属于另一个用户。 - artem

1
if (!packageInfo.sourceDir.toLowerCase().startsWith("/system/"))

3
这并不一定是真的,你总是可以使用平台密钥(如果你有访问权限)签署应用程序,并使用系统sharedUserId安装它,这样它就会作为系统应用运行。 - Monu Surana

0

这是我为此目的编写的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;
    }
}

0

您可以使用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 实现一些信任链来知道所有证书是否确实有效。 - Edw590

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