Как проверить, является ли приложение несистемным в Android?

Я получаю список ApplicationInfo объектов с packageManager. getInstalledApplications(0) и попытаться классифицировать их по тому, являются ли они системными приложениями.

Некоторое время я использовал метод, описанный /6044499#comment10955328_6044499">здесь, однако, увидев, что в моем приложении некоторые приложения не были в списке несистемных приложений (например, Facebook, который, когда он доступен, запрашивает система для установки на SD-карту). После следующего прочтения фактической документации для ApplicationInfo.FLAG_SYSTEM, и понимая, что на самом деле он не фильтрует системные приложения, теперь я ищу новый подход.

Я предполагаю, что существует большой разрыв между UID системных и несистемных приложений, которые я могу собрать, чтобы провести это различие, но пока я не нашел ответа. Я также изучал другие флаги, такие как ApplicationInfo.FLAG_EXTERNAL_STORAGE, однако я поддерживаю API 1.5.

У кого-нибудь есть реальное решение этой проблемы (не связанное с FLAG_SYSTEM)?


person Phil    schedule 09.01.2012    source источник


Ответы (13)


У меня сложилось впечатление, что все приложения в образе системы являются системными приложениями (и обычно устанавливаются в /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);
}
person sergio91pt    schedule 02.02.2013

Вы можете проверить подпись приложения, которое оно подписало системой. Как ниже

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

Вы можете получить больше кода в моем блоге Как проверить, является ли приложение системным или нет (по подписанной подписи)

person Pankaj Kumar    schedule 08.07.2014
comment
У меня не было возможности попробовать это, но мне очень нравится этот подход, если он работает - определенно отличный способ сделать сравнение. - person Phil; 08.07.2014
comment
@Phil Было бы приятно узнать, что это оправдало ваши ожидания. Спасибо. - person Pankaj Kumar; 09.07.2014
comment
@PankajKumar: я разработал системное приложение для вкладки iBall, и я использовал оба метода в соответствии с вашим блогом, и я получаю возвращаемое значение как «истина» в обоих случаях (поэтому я надеюсь, что мое приложение действительно стало системным приложением :) ). Два сомнения, (1) ваш второй метод «isAppPreLoaded» делает то же самое, что и «isSystemApp», в дополнение к проверке, предварительно загружено приложение или нет, верно? ;(2) В чем смысл предварительной загрузки, означает ли это, что он проверяет, находится ли приложение в системе/приложении, или проверяет, что приложение запекается вместе с ПЗУ или и то, и другое? - person Basher51; 14.08.2014
comment
@ Basher51 Иногда приложения предустановлены, но это не системное приложение. Например, вы разработали приложение, подписанное вашим сертификатом и добавленное к пользовательскому коду AOSP, которое подписано сертификатом производителя. В этом случае ваше приложение предустановлено, но это не системное приложение (с учетом сертификата). Таким образом, последний метод в блоге делает и то, и другое: сначала проверяет, установлено ли приложение предварительно, если да, то проверяет подпись приложения. Надеюсь теперь понятно :)\ - person Pankaj Kumar; 14.08.2014
comment
@PankajKumar: Не совсем понятно. Почему вы говорите, что в этом случае ваше приложение предустановлено, но это не системное приложение (учитывая сертификат)? Мое приложение находится в системе/приложении и подписано теми же сертификатами, что и система. Так что теперь это не системное приложение. Также я запустил оба «метода» в соответствии с вашим блогом и дал возвращаемое значение «истина» .Значит, это не подтверждает, что мое приложение стало системным? - person Basher51; 14.08.2014
comment
@ Basher51 Ваше приложение является системным. Посмотрите, в этом случае вы создаете приложение и подписываете его своим сертификатом (не тем сертификатом, которым подписывается код AOSP). До сих пор ваше приложение похоже на стороннее приложение. Правильно? Теперь вы копируете этот apk в system/app. Этот шаг не делает ваше приложение системным (существует слишком много API, к которым ваше приложение не может получить доступ, даже если оно находится в системном каталоге). Для этого он должен быть подписан тем же сертификатом, ЧТО ВЫ ДЕЛАЕТЕ, и местоположение должно быть /system/app. Не беспокойтесь о вашем случае, вы делаете правильный путь. - person Pankaj Kumar; 14.08.2014
comment
@ПанкайКумар. Я попробовал ваш метод, он работает в некоторых случаях, но для нескольких пакетов он говорит, что это не системное приложение ... например, com.google.android.gms, com.sec.android.app.launcher, com.google.android.googlequicksearchbox они не системное приложение? - person sandy; 20.04.2015
comment
@sandy Нет, это не системные приложения. Это приложения от Google, и они подписаны сертификатом Google, а не сертификатом производителя. Даже эти приложения установлены в другом месте, а не в /system/app. Теперь я ясно? - person Pankaj Kumar; 20.04.2015
comment
@sandy adb shell pm path com.google.android.gms попробуйте войти в командное окно, и вы получите расположение этого пакета. - person Pankaj Kumar; 20.04.2015
comment
очень интересно - person Vlad; 07.07.2018

Существует два типа несистемных приложений:

  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));
    }
}
person Kushal    schedule 27.01.2016

Что ж, на мой взгляд, это небрежное решение (что, если /data/app не является каталогом приложений на всех устройствах?), но после тщательного поиска я пришел к следующему:

for (ApplicationInfo ai : appInfo) {
    if (ai.sourceDir.startsWith("/data/app/")) {
        //Non-system app
    }
    else {
        //System app
    }
}
person Phil    schedule 09.01.2012
comment
Если вы обновите системное приложение, оно поместит обновление в каталог /data/app. Но это все еще системное приложение. - person Ion Aalbers; 26.11.2012
comment
@IonAalbers: У меня тоже были такие сомнения. В большинстве случаев написано, что системные приложения находятся в системе/приложении, а пользовательские приложения — в данных/приложении. Почему при обновлении системы/приложения его apk сохраняется в данных/приложении? Пожалуйста, объясните немного больше? - person Basher51; 14.08.2014
comment
@ Basher51 Приложение, местоположение которого /system/app/, не означает, что это системное приложение. Это означает, что приложение предварительно загружено. И для сомнения в приложении обновления, я могу предположить, что они написали эту логику, чтобы иметь в виду factory-reset. Если приложение будет обновляться в том же месте, будет трудно найти исходное состояние приложения при сбросе настроек. - person Pankaj Kumar; 14.08.2014
comment
ВООБЩЕ НЕ РАБОТАЛ. В сторонних приложениях каталог также начинается с /data/app/. - person Vlad; 07.07.2018

Если приложение является несистемным приложением, оно должно иметь намерение запуска, с помощью которого оно может быть запущено. Если намерение запуска равно нулю, то это системное приложение.

Пример системных приложений: «com.android.browser.provider», «com.google.android.voicesearch».

Для вышеуказанных приложений вы получите NULL при запросе запуска Intent.

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
    }
}
person Darshan Patel    schedule 19.08.2013
comment
Это гениально. Это хороший способ найти запускаемые приложения. - person Chris Feist; 03.10.2013
comment
Многие системные приложения имеют намерения запуска, например. com.android.settings - person Alex Cohn; 23.02.2014
comment
Очень хороший ответ - я пока не мог понять, как запускаемые приложения можно рассматривать как системные приложения. - person Björn Hallström; 16.06.2021

Здесь есть небольшое недоразумение. Для Android понятие «системное приложение» — это то, что устанавливается в образ системы, оно ничего не говорит о том, от какого разработчика оно было получено. Таким образом, если OEM-производитель решит предварительно загрузить Facebook в образ системы, это будет системное приложение, и оно останется таковым, независимо от того, где устанавливаются обновления для приложения. Они точно не будут установлены на образ системы, потому что он доступен только для чтения.

Итак, ApplicationInfo.FLAG_SYSTEM верен, но, похоже, это не тот вопрос, который вы задаете. Я думаю, вы спрашиваете, подписан ли пакет системным сертификатом. Что не обязательно является хорошим индикатором чего-либо, это может варьироваться от устройства к устройству, и некоторые неожиданные компоненты на ванильном Android не подписаны системным сертификатом, хотя вы могли бы ожидать, что они будут подписаны.

В более новых версиях Android есть новый путь /system/priv-app/, который пытается быть местом установки «настоящих» системных приложений. Приложения, предварительно загруженные в образ системы, попадают в /system/app/. См. раздел Привилегированное приложение AOSP и системное приложение.

person cyngus    schedule 23.06.2014
comment
Хорошая перспектива. Я создал системное приложение, и оно находится в системе/приложении. Я подписал его системными ключами (по крайней мере, на это я надеюсь). Но есть ли способ (программно или иным образом), чтобы полностью подтвердить, что мое приложение теперь стало настоящим системным приложением (которое подписано теми же ключами, что были запечены в ПЗУ)? - person Basher51; 14.08.2014

Это упрощенная и более эффективная версия других ответов, перечисленных здесь. Это более эффективно, если вы просто выполняете итерацию непосредственно по 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
        }
    }
person Tyler    schedule 08.08.2019

Вот различные возможные способы узнать, является ли приложение системным по имени пакета (используются некоторые коды в этом посте).

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;
    }
}
person Vinayak Bevinakatti    schedule 29.12.2017
comment
Как приложение может быть isSystemSigned? Нужен ли мне системный ключ для этого? - person lucky1928; 11.06.2018
comment
isSystemSigned означает подписание вашего apk ключами платформы. Обратитесь к этому для создания сборки для разработки - github.com/aosp- зеркало/платформа_сборка/дерево/мастер/цель/ - person Vinayak Bevinakatti; 13.06.2018
comment
Если у вас есть дополнительные вопросы, эти сообщения могут ответить на некоторые из них - stackoverflow.com/questions/37586255/ и stackoverflow.com/a /3651653/28557 - person Vinayak Bevinakatti; 13.06.2018

Если у вас есть файл APK и вы хотите проверить, является ли это системным приложением или пользователем установлена ​​простая логика: - Файлы системного приложения недоступны для записи

private boolean isSystemApkFile(File file){
   return !file.canWrite();
}
person shivampip    schedule 27.12.2016
comment
Несистемные файлы APK также недоступны для записи, поскольку они принадлежат другому пользователю. - person artem; 22.09.2018

Вот 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;
    }
}
person AivarsDa    schedule 26.07.2016

Вы можете использовать checkSignatures, чтобы определить, является ли приложение системным или нет.

Все системные приложения подписаны одним и тем же ключом.

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

А подписанный системным ключом пакет "андроид".

    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")
    }
person Blundell    schedule 19.05.2020

person    schedule
comment
@Ocool, я специально указываю в своем вопросе, что FLAG_SYSTEM на самом деле не работает, так что это не то, что я ищу. - person Phil; 09.01.2012
comment
@Phil Я знаю, что FLAG_SYSTEM не работает сам по себе, поэтому условие &, попробуйте, у меня это сработало. - person Nitin; 11.01.2012
comment
@Ocool, это то, чем я пользуюсь некоторое время, и оно похоже работает, пока вы не заметите, что некоторые приложения отсутствуют. Дело в том, что FLAG_SYSTEM просто получает приложения, которые установлены в образ системы устройства. Это означает, что приложения не будут установлены на SD-карту и т. д. Лучший способ получить все приложения — это, к сожалению, проверить их каталог (/data/apps). - person Phil; 11.01.2012
comment
@Phil Приложения на SD-карте всегда являются пользовательскими приложениями, а обновления системных приложений также находятся в /data/apps. - person sergio91pt; 02.02.2013

person    schedule
comment
Это не обязательно верно, вы всегда можете подписать приложение ключом платформы (если у вас есть доступ к нему) и установить его с системным sharedUserId, и оно будет работать как системное приложение. - person Monu Surana; 13.09.2016