2012-01-09 24 views
30

Ho ottenuto un elenco di ApplicationInfo oggetti con packageManager.getInstalledApplications(0) e tentando di categorizzarli in base al fatto che siano o meno un'applicazione di sistema.Come faccio a verificare se un'app è un'app non di sistema in Android?

Per un po 'ho utilizzato la tecnica descritta here, però dopo aver visto che nella mia richiesta, alcune delle applicazioni non erano nella lista di applicazioni non-sistema (come Facebook, che quando disponibile chiede al sistema per installarsi sulla scheda SD). Dopo aver letto la documentazione attuale per ApplicationInfo.FLAG_SYSTEM e aver capito che in realtà non filtra le app di sistema, ora sto cercando un nuovo approccio.

La mia ipotesi è che ci sia un grande divario tra UID di Sistema e app non di sistema che posso raccogliere per fare questa distinzione, ma fino ad ora non ho trovato una risposta. Ho anche esaminato altre flag, ad esempio ApplicationInfo.FLAG_EXTERNAL_STORAGE, tuttavia sto supportando l'API 1.5.

Qualcuno ha una soluzione reale a questo (non coinvolge FLAG_SYSTEM)?

risposta

6

bene, è una soluzione sciatta, a mio parere (e se /data/app non è la directory di applicazioni su tutti i dispositivi?), Ma dopo una ricerca approfondita, questo è ciò che ho fornito con:

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

Se si aggiorna un'app di sistema, l'aggiornamento verrà inserito nella directory/data/app. Ma è ancora un'app di sistema. –

+0

@IonAalbers: ho avuto anche questo dubbio. La maggior parte pone le app di sistema che risiedono nel sistema/app e nelle app utente in data/app. Perché quando il sistema/app viene aggiornato il suo apk viene memorizzato in data/app? Potresti spiegarci un po 'di più? – Basher51

+1

@ Basher51 L'applicazione il cui percorso è '/ system/app /' non significa che sia app di sistema. Significa che l'applicazione è precaricata. E per i dubbi sulle applicazioni di aggiornamento, posso indovinare, hanno scritto questa logica per tenere a mente il ripristino delle impostazioni di fabbrica. Se l'applicazione verrà aggiornata nella stessa posizione, sarà difficile trovare lo stato dell'applicazione originale durante il ripristino delle impostazioni di fabbrica. –

22
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);   
    } 
} 
+0

@Ocool, ho espressamente affermo nella mia domanda che 'FLAG_SYSTEM' in realtà non funziona, quindi questo non è quello che sto cercando. – Phil

+0

@Phil So che FLAG_SYSTEM non funziona da solo perché la condizione "&", prova, ha funzionato per me. – Nitin

+0

@Ocool, questo è quello che uso da un po ', e * sembra * funzionare, finché non si nota che alcune app sono mancanti. Il fatto è che "FLAG_SYSTEM' ottiene semplicemente le app che sono [installate nell'immagine di sistema del dispositivo] (http://developer.android.com/reference/android/content/pm/ApplicationInfo.html#FLAG_SYSTEM). Ciò significa che non verranno installate app sulla scheda SD, ecc. Il modo migliore per ottenere ** tutte ** le app è sfortunatamente controllare la loro directory ("/ data/apps"). – Phil

21

ero sotto l'impressione che tutte le applicazioni nell'immagine di sistema sono applicazioni di sistema (e normalmente installato in /system/app).

Se FLAG_SYSTEM è impostato solo per le applicazioni di sistema, questo sistema funziona anche per applicazioni in storage esterno:

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

Un'alternativa è quella di utilizzare il programma pm riga di comando nel telefono.

Sintassi:

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. 

Codice:

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

Poi:

boolean isUserApp(String pckg) { 
    return !mSystemApps.contains(pckg); 
} 
+0

Ha funzionato come previsto, grazie –

4

Se un'applicazione è un'applicazione non di sistema deve avere un intento di lancio con il quale si può essere lanciato. Se l'intento di lancio è nullo, allora è un'app di sistema.

Esempio di app di sistema: "com.android.browser.provider", "com.google.android.voicesearch".

Per le applicazioni di cui sopra si otterrà NULL quando si esegue una query per l'intento di lancio.

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 
    } 
} 
+0

Questo è geniale. Questo è un buon modo per trovare le applicazioni in sospeso. – Chris

+4

Molte app di sistema hanno intenti di lancio, ad es. com.android.settings –

2
if (!packageInfo.sourceDir.toLowerCase().startsWith("/system/")) 
+0

Questo non è necessariamente vero, puoi sempre firmare un'app con la chiave di piattaforma (dato che hai accesso ad una) e installarla con system ** sharedUserId ** e funzionerà come un'app di sistema –

5

C'è un po 'di malinteso. Per Android la nozione di "app di sistema" è quella che viene installata nell'immagine di sistema, si dice niente su quale sviluppatore proviene. Quindi, se un OEM decide di precaricare Facebook sull'immagine del sistema, è un'app di sistema e continuerà a esserlo, indipendentemente da dove verranno installati gli aggiornamenti dell'app.Non verranno installati nell'immagine di sistema, perché è di sola lettura.

Quindi ApplicationInfo.FLAG_SYSTEM è corretto, ma non sembra essere la domanda che stai ponendo. Penso che tu stia chiedendo se un pacchetto è firmato con il certificato di sistema. Quale non è necessariamente un buon indicatore di qualcosa, questo può variare da dispositivo a dispositivo e alcuni componenti sorprendenti su Android vaniglia non sono firmati con il certificato di sistema, anche se ci si potrebbe aspettare che siano.

Nelle versioni più recenti di Android è disponibile un nuovo percorso/system/priv-app/che tenta di essere il percorso di installazione per le app di sistema "reali". Le app che sono appena pre-caricate nell'immagine di sistema terminano in/system/app /. Vedi AOSP Privileged vs System app

+0

Bella prospettiva . Ho creato un'app di sistema e risiede nel sistema/app. L'ho firmata con le chiavi di sistema (almeno questo è quello che spero). Ma c'è un modo (programmaticamente o meno) per confermare completamente che la mia app ha ora diventare una vera app di sistema (che è firmata con le stesse chiavi che erano state preparate nella ROM)? – Basher51

12

È possibile verificare la firma dell'applicazione che ha firmato con il sistema. Come sotto

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

È possibile ottenere ulteriori codice sul mio blog How to check if application is system app or not (By signed signature)

+0

Non ho avuto la possibilità di provare questo, ma mi piace molto questo approccio se funziona - sicuramente un ottimo modo per fare il confronto. – Phil

+0

@Phil Sarebbe il mio piacere sapere che ha funzionato per le vostre aspettative. Grazie. –

+0

@PankajKumar: Ho sviluppato un'app di sistema per la scheda iBall e ho usato entrambi i metodi come per il tuo blog e ottengo il valore restituito come "vero" in entrambi i casi (quindi spero che la mia app sia diventata un'applicazione di sistema:)). Due dubbi, (1) il tuo secondo metodo 'isAppPreLoaded' fa la stessa cosa di 'isSystemApp' oltre a verificare se l'app è precaricata o meno, giusto? (2) Qual è il significato di precaricato, vuol dire che controlla se l'app risiede nel sistema/app o controlla che l'app sia stata cotta con la ROM o entrambe? – Basher51

1

Ci sono 2 tipi di non - Sistema applicazioni:

  1. App da scaricare dal Google Play Store
  2. Applicazioni precaricate nel dispositivo dal produttore

Sotto codice sarà tornare elenco di tutte le applicazioni di cui sopra:

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

Ecco un AppUtil che ho scritto per questo scopo. esempio
Usage:

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; 
    } 
} 
1

Se avere un file APK e voler controllare è app di sistema o utente installato una logica semplice: - file sistema app sono non scrivibile

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

Qui sono diff erenti modi possibile vedere se l'applicazione è un'applicazione di sistema per il nome del pacchetto (usato alcuni dei codici in questo post)

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