Question

I am getting a list of ApplicationInfo Objects with packageManager.getInstalledApplications(0) and attempting to categorize them by whether or not they are a system application.

For a while I have been using the technique described here, however after seeing that in my application, some of the apps were not in the non-system apps list (such as Facebook, which when available asks the system to install itself on the SD card). After next reading the actual documentation for ApplicationInfo.FLAG_SYSTEM, and understanding that it doesn't actually filter system apps, I am now looking for a new approach.

My guess is that there is a large gap between UIDs of System and non-system apps that I can gather to make this distinction, but as of yet I have not found an answer. I also looked into other flags, such as ApplicationInfo.FLAG_EXTERNAL_STORAGE, however I am supporting API 1.5.

Does anyone have a real solution to this (not involving FLAG_SYSTEM)?

Was it helpful?

Solution 5

Well, it's a sloppy solution in my opinion (what if /data/app isn't the apps directory on all devices?), but after a thorough search, this is what I have come up with:

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

OTHER TIPS

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

I was under the impression that all apps in the system image are system apps (and normally installed in /system/app).

If FLAG_SYSTEM is only set to system applications, this will work even for apps in external storage:

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

An alternative is to use the pm command-line program in your phone.

Syntax:

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.

Code:

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

Then:

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

You can check the signature of application which it signed with system. Like below

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

You can get more code on my blog How to check if application is system app or not (By signed signature)

There are 2 type of Non - system applications :

  1. Apps downloaded from Google Play Store
  2. Preloaded apps by device manufacturer

This code will return a list of all above applications:

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

There is a little bit of misunderstanding here. For Android the notion of a "system app" is one that is install on the system image, it says nothing about what developer it came from. So, if an OEM decides to preload Facebook on to the system image, it is a system app and will continue to be so, regardless of where updates to the app get installed. They won't get installed on the system image, for sure, because it is read-only.

So ApplicationInfo.FLAG_SYSTEM is correct, but that doesn't seem to be the question you are asking. I think you're asking if a package is signed with the system certificate. Which is not necessarily a good indicator of anything, this may vary from device to device and some surprising components on vanilla Android are not signed with the system certificate, even though you might expect them to be.

In newer versions of Android there is a new path, /system/priv-app/ that attempts to be the install location for "real" system apps. Apps that are just pre-loaded on the system image then end up in /system/app/. See AOSP Privileged vs System app

If an Application is a non-system application it must have a launch Intent by which it can be launched. If the launch intent is null then its a system App.

Example of System Apps: "com.android.browser.provider", "com.google.android.voicesearch".

For the above apps you will get NULL when you query for launch 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
    }
}

This is a simplified and more efficient version of other responses listed here. It is more efficient if you just iterate directly over the 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
        }
    }

Here are different possible ways to see if the app is a system app by its package name (used some of the codes in this 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;
    }
}
if (!packageInfo.sourceDir.toLowerCase().startsWith("/system/"))

If having an APK file and want to check is it System app or User installed a Simple logic:- System app Files are not writable

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

Here is an AppUtil I wrote for that purpose.
Usage example:

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

You can use checkSignatures to determine if an app is a system app or not.

All system apps are signed with the same key.

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

And signed with the system key is the "android" package.

    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")
    }
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top