我收到ApplicationInfo
个ApplicationInfo.FLAG_EXTERNAL_STORAGE
对象的列表,并试图根据它们是否是系统应用程序对它们进行分类。
有一段时间我一直在使用packageManager.getInstalledApplications(0)所描述的技术,但是在我的应用程序中看到这一点之后,一些应用程序不在非系统应用程序列表中(例如 Facebook ,当可用时要求系统将其自身安装在SD卡上)。在下次阅读here的实际文档并了解它实际上并未过滤系统应用程序之后,我现在正在寻找一种新方法。
我的猜测是,系统和非系统应用程序的UID之间存在很大差距,我可以收集这些差异以进行区分,但到目前为止我还没有找到答案。我还查看了其他标志,例如FLAG_SYSTEM
,但我支持API 1.5。
有没有人真正解决这个问题(不涉及{{1}})?
答案 0 :(得分:25)
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);
}
}
答案 1 :(得分:23)
我的印象是系统映像中的所有应用都是系统应用(通常安装在/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);
}
答案 2 :(得分:16)
您可以检查它与系统签名的应用程序的签名。如下所示
/**
* 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)
获取更多代码答案 3 :(得分:6)
嗯,在我看来这是一个草率的解决方案(如果 / data / app 不是所有设备上的应用程序目录怎么办?),但经过彻底搜索后,这就是我来的起来:
for (ApplicationInfo ai : appInfo) {
if (ai.sourceDir.startsWith("/data/app/")) {
//Non-system app
}
else {
//System app
}
}
答案 4 :(得分:5)
如果应用程序是非系统应用程序,则它必须具有启动Intent才能启动它。如果启动意图为null,则为系统App。
系统应用程序示例:“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
}
}
答案 5 :(得分:5)
这里有一点点误解。对于Android,“系统应用程序”的概念是安装在系统映像上的概念,它说 nothing 关于它来自哪个开发人员。因此,如果OEM决定将Facebook预加载到系统映像,它就是一个系统应用程序,并且将继续如此,无论应用程序的更新安装在何处。它们不会安装在系统映像上,因为它是只读的。
所以ApplicationInfo.FLAG_SYSTEM是正确的,但这似乎不是你问的问题。我想你问的是一个软件包是否与系统证书签名。这不一定是任何事情的良好指标,这可能因设备而异,并且vanilla Android上的一些令人惊讶的组件未使用系统证书签名,即使您可能期望它们。
在较新版本的Android中,有一条新路径/ system / priv-app /试图成为“真实”系统应用程序的安装位置。刚刚加载到系统映像上的应用程序最终会出现在/ system / app /中。见AOSP Privileged vs System app
答案 6 :(得分:5)
有两种非系统应用程序:
此代码将返回以上所有应用程序的列表:
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));
}
}
答案 7 :(得分:2)
以下是通过其包名查看应用程序是否为系统应用程序的不同可能方法(使用此帖子中的一些代码)
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;
}
}
答案 8 :(得分:2)
这是此处列出的其他响应的简化且更有效的版本。如果直接在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 :(得分:1)
if (!packageInfo.sourceDir.toLowerCase().startsWith("/system/"))
答案 10 :(得分:1)
如果有一个APK文件并想要检查它是系统应用程序还是用户安装 一个简单的逻辑: - 系统应用文件无法写入
private boolean isSystemApkFile(File file){
return !file.canWrite();
}
答案 11 :(得分:1)
所以我想把我利用这个线程和其他一些线程的知识制作的实用程序类放在这里。但在我继续之前,对一些术语的解释,如果我没听错的话,从那个类复制,用于它。
<块引用>在 KitKat 4.4 以下,/system/app 中的所有应用程序都被授予特权 权限。甚至计算器应用程序也有它们。那可能是一个 安全漏洞。所以他们被区分为普通和 特权系统应用程序和普通应用程序没有特权 KitKat 4.4 以上的权限。所以这些实用程序考虑到了这一点。 他们还考虑了以下名称:
系统分区说明:直到 Oreo 8.1,还有 只有一个:/system。从 Pie (9) 开始,还有 /vendor 和 /产品。
考虑到这一点,这里有两个功能:
/**
* <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;
}
}
}
*/
}
答案 12 :(得分: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;
}
}
答案 13 :(得分:0)
您可以使用checkSignatures
来确定某个应用程序是否为系统应用程序。
所有系统应用程序都使用相同的密钥签名。
并使用系统密钥签名的是“ 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")
}