tarjeta - ¿Cómo verifico si una aplicación es una aplicación que no es del sistema en Android?
programar nfc android (11)
Recibo una lista de Objetos de ApplicationInfo
con packageManager.getInstalledApplications(0) e intento categorizarlos según sean o no una aplicación del sistema.
Durante un tiempo he estado usando la técnica descrita here , sin embargo, después de ver que en mi aplicación, algunas de las aplicaciones no estaban en la lista de aplicaciones que no son del sistema (como Facebook , que cuando está disponible le pide al sistema que se instale en la SD). tarjeta). Después de leer la documentación real de ApplicationInfo.FLAG_SYSTEM y entender que en realidad no filtra las aplicaciones del sistema, ahora estoy buscando un nuevo enfoque.
Mi conjetura es que hay una gran brecha entre los UID del sistema y las aplicaciones que no son del sistema que puedo reunir para hacer esta distinción, pero hasta ahora no he encontrado una respuesta. También busqué en otras banderas, como ApplicationInfo.FLAG_EXTERNAL_STORAGE
, sin embargo, soy compatible con API 1.5.
¿Alguien tiene una solución real para esto (que no implique FLAG_SYSTEM
)?
Aquí hay diferentes formas posibles de ver si la aplicación es una aplicación del sistema por su nombre de paquete (se usan algunos de los códigos en esta publicación)
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;
}
}
Aquí hay una aplicación que escribí para ese propósito.
Ejemplo de uso:
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;
}
}
Bueno, en mi opinión, es una solución descuidada (¿qué pasa si / data / app no es el directorio de aplicaciones en todos los dispositivos?), Pero después de una búsqueda exhaustiva, esto es lo que he encontrado:
for (ApplicationInfo ai : appInfo) {
if (ai.sourceDir.startsWith("/data/app/")) {
//Non-system app
}
else {
//System app
}
}
Hay 2 tipos de aplicaciones que no son del sistema :
- Aplicaciones descargadas de Google Play Store
- Aplicaciones precargadas por fabricante del dispositivo
Este código devolverá una lista de todas las aplicaciones anteriores:
if (!packageInfo.sourceDir.toLowerCase().startsWith("/system/"))
Hay un poco de malentendido aquí. Para Android, la noción de una "aplicación del sistema" es una que se instala en la imagen del sistema, no dice nada de qué desarrollador proviene. Entonces, si un OEM decide precargar Facebook en la imagen del sistema, es una aplicación del sistema y seguirá siéndolo, independientemente de dónde se instalen las actualizaciones de la aplicación. No se instalarán en la imagen del sistema, seguro, porque es de solo lectura.
Entonces, ApplicationInfo.FLAG_SYSTEM es correcto, pero esa no parece ser la pregunta que está haciendo. Creo que estás preguntando si un paquete está firmado con el certificado del sistema. Lo que no es necesariamente un buen indicador de nada, esto puede variar de un dispositivo a otro y algunos componentes sorprendentes en el Android de vainilla no están firmados con el certificado del sistema, aunque podría esperar que lo estén.
En las versiones más recientes de Android hay una nueva ruta, / system / priv-app / que intenta ser la ubicación de instalación de las aplicaciones del sistema "real". Las aplicaciones que se cargan previamente en la imagen del sistema terminan en / system / app /. Ver la aplicación AOSP Privileged vs System
Puede comprobar la firma de la solicitud que firmó con el sistema. Como abajo
/**
* 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;
}
}
Puede obtener más código en mi blog Cómo verificar si la aplicación es una aplicación del sistema o no (mediante firma firmada)
Si tiene un archivo APK y desea verificar si la aplicación del sistema o el usuario instaló una lógica simple: - Los archivos de la aplicación del sistema no se pueden escribir
private boolean isSystemApkFile(File file){
return !file.canWrite();
}
Si una Aplicación es una aplicación que no es del sistema, debe tener una Intención de lanzamiento mediante la cual se pueda iniciar. Si el intento de lanzamiento es nulo, entonces es una aplicación de sistema.
Ejemplo de aplicaciones del sistema: "com.android.browser.provider", "com.google.android.voicesearch".
Para las aplicaciones anteriores, obtendrá NULL cuando solicite Intent de lanzamiento.
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
}
}
Tenía la impresión de que todas las aplicaciones en la imagen del sistema son aplicaciones del sistema (y normalmente instaladas en /system/app
).
Si FLAG_SYSTEM
solo está configurado para aplicaciones del sistema, esto funcionará incluso para aplicaciones en almacenamiento externo:
boolean isUserApp(ApplicationInfo ai) {
int mask = ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
return (ai.flags & mask) == 0;
}
Una alternativa es usar el programa de línea de comando pm
en su teléfono.
Sintaxis:
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.
Código:
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();
Entonces:
boolean isUserApp(String pckg) {
return !mSystemApps.contains(pckg);
}
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));
}
}
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);
}
}