Android 5.1.1 and above - getRunningAppProcesses() returns my application package only
Asked Answered
G

7

109

It seems Google finally closed all doors for getting the current foreground application package.

After the Lollipop update, which killed getRunningTasks(int maxNum) and thanks to this answer, I used this code to get the foreground application package since Lollipop:

final int PROCESS_STATE_TOP = 2;
RunningAppProcessInfo currentInfo = null;
Field field = null;
try {
    field = RunningAppProcessInfo.class.getDeclaredField("processState");
} catch (Exception ignored) { 
}
ActivityManager am = (ActivityManager) this.getSystemService(Context.ACTIVITY_SERVICE);
List<RunningAppProcessInfo> appList = am.getRunningAppProcesses();
for (RunningAppProcessInfo app : appList) {
    if (app.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND &&
        app.importanceReasonCode == 0 ) {
        Integer state = null;
        try {
            state = field.getInt( app );
        } catch (Exception ignored) {
        }
        if (state != null && state == PROCESS_STATE_TOP) {
            currentInfo = app;
            break;
        }
    }
}
return currentInfo;

Android 5.1.1 and above (6.0 Marshmallow), it seems, killed getRunningAppProcesses() as well. It now returns a list of your own application package.


UsageStatsManager

We can use the new UsageStatsManager API as described here but it doesn't work for all applications. Some system applications will return the same package

com.google.android.googlequicksearchbox

AccessibilityService (December 2017: Going to be banned for use by Google)

Some applications use AccessibilityService (as seen here) but it has some disadvantages.


Is there another way of getting the current running application package?

Griselgriselda answered 3/6, 2015 at 11:52 Comment(18)
Regarding the problems you have with UsageStatsManager, did you have the same problems with an accessibility service or getRunningAppProcesses in Android L?Dosser
@Dosser no. Tested AccessibilityService with my app on Android M and it seems to get the right package even for those system apps UsageStats didn't. However, there's a weird timing issue when using the Accessibility way, it sometimes report a different package twice, after it already reported the right package.Griselgriselda
Du Speed Booster seems work. I 'm not sure if it uses UsageStatsManager.Footstool
Note that several major vendors have removed the system activity that grants access to the UsageStats API from their devices. This means that apps on those devices can never acquire the necessary permission.Hinojosa
Samsung being one of them. That's over 60% of the market.Hinojosa
Here is a ticket from the android bug tracker regarding this issue: code.google.com/p/android-developer-preview/issues/…Orpine
If I parse the output of running ps in a shell and the policy is "fg" and the contents of /proc/[pid]/oom_adj_score equals 0 then the app is the foreground application. Unfortunately, it seems /proc/[pid]/oom_adj_score is no longer readable on Android 6.0. gist.github.com/jaredrummler/7d1498485e584c8a120ePhung
Will someone please run the script I just put in my answer below? Thanks.Phung
@JaredRummler I tested your code on Android 5.1. I only had to make a few adjusts as I commented in your answer. Have you confirmed if it will work on Android 6.0?Bolt
Could you add more info what you need the info for? Maybe it is enough to find if your own app is in the foreground, which can easily be done with a static variable in the activity.Gibraltar
@bjornson to find if my own app is in foreground/background you can use ActivityLifecycleCallbacks . no need for static variable. I need this to know which app is running in foreground, as explained in the question.Griselgriselda
Ok, thanks for claryfying, but you might have a problem with Androids new Security concept. Not quite getting the ActivityLifecycleCallbacks though. How can I check within a Service class if my Main Activity is in the foreground using ActivityLifecycleCallbacks? I am using a static variable in my visible activity so In a Service that listens to Google Cloud Messaging I can decide if I need a notification or simply tell the view to refresh.Gibraltar
@bjornson I'm not quite sure I understand your specific requirements but there are lots of example on how to use ActivityLifecycleCallbacks on StackOverflow.Griselgriselda
@LiorIluz Can you list the disadvantages of using the Accessibility service provided by android?Towards
@Towards the disadvantages are well described here #3874159 also linked to in my question. I chose to use it in spite of its disadvantages, mainly because there's no better alternative for now.Griselgriselda
Any workaround for Nougat devices?Biddick
Possible duplicate of Android: How can I get the current foreground activity (from a service)?Dosser
I'm voting to close this as a duplicate because there are way too many questions about this on StackOverflow and the original question covers most of the techniques pretty well.Dosser
P
96

To get a list of running processes on Android 1.6 - Android 6.0 you can use this library I wrote: https://github.com/jaredrummler/AndroidProcesses The library reads /proc to get process info.

Google has significantly restricted access to /proc in Android Nougat. To get a list of running processes on Android Nougat you will need to use UsageStatsManager or have root access.

Click the edit history for previous alternative solutions.

Phung answered 3/9, 2015 at 3:27 Comment(32)
Does using "libsuperuser" mean you need root in order to get the recent/running apps on Android 6 ?Selfdeprecating
@androiddeveloper no, I only used libsuperuser because it provides an easy way to run a shell command (non-root or root) and get the output. I can re-write it without libsuperuser if there is enough demand.Phung
I see. Is the list of processes ordered in some way? It's the list of currently running apps, right? Also, what's the difference between using this command, and using "top" ? Both have about the same output, no?Selfdeprecating
@androiddeveloper It is ordered by pid. It is basically the same as top. I used ps because I know it has been apart of /system/bin/toolbox for along time. I believe both ps and top get info from /proc/[pid]/stat.Phung
I actually don't know about "toolbox" command. Isn't "top" more used? About being sorted by pid, is there a way to make it be sorted by something else?Selfdeprecating
@androiddeveloper I think we are wasting comment space here. Have a look at the source for top and ps. github.com/android/platform_system_core/tree/master/toolbox If you want to continue any discussion my email is in my profile. Basically, ps is a little faster.Phung
Sorry for that. Was just being curious. :(Selfdeprecating
@JaredRummler I'm currently in the process of implementing your solution into my app. Seems to be working fine as far as I can tellShaunta
If you're looking for a particular package name, getForeGroundApp may need to be modified to trim whitespace by changing the return statement as such: return foregroundProcess.trim(); Otherwise there seems t be trailing white space. All and all using the oom values is brilliant! @JaredRummlerChlorite
Works pretty well, but a bit too well. Keeps giving other processes in between at random, have to hack around a little bit to check if the app you want to look at is the foreground process.Adjunction
@androiddeveloper, If you want to sort it based on a different attribute, make Process implement Comparable and override the compareTo method. Then, when you use Collections.sort(processesList), it will use the order you specified.Barnard
@BruceWayne First, you don't need to do it this way, as you have sort function with the Comparator parameter (example here: developer.android.com/reference/java/util/Arrays.html#sort(T[], java.util.Comparator<? super T>) ). Second, I wanted to sort by time launched, which I can't find there.Selfdeprecating
@androiddeveloper, That works too, if you're not convinced that the natural ordering for a Process is the lexical order of its name, then use a comparator like you said.Barnard
@BruceWayne It depends how you wish to sort.Selfdeprecating
@JaredRummler, Could you elaborate on what if SELinux is enforcing means?Barnard
Is there a solution without using libsuperuser? If there is I would be very grateful.Foreleg
Testing on Moto G: I had to change if (lines.length != 2) to if (lines.length != 3) and String cpuaccctSubsystem = lines[1]; to String cpuaccctSubsystem = lines[2];. Also, I added a trim() in the return statement, so it eliminates some junk. Anyone else tested the hack code without the lib in any other device?Bolt
@BruceWayne I think Jared is refering to this link: https://source.android.com/devices/tech/security/selinux/index.htmlBolt
Is there any way to prevent this message from displaying when it's looping through the files? avc: denied { getattr } for path="/proc/18" dev="proc" ino=10667 scontext=u:r:untrusted_app:s0:c512,c768 tcontext=u:r:kernel:s0 tclass=dir permissive=0Foreleg
This is a very interesting workaround. However, when running the command toolbox ps, the first line returned (to stdout) will be the header and should be discarded else it will result in an exception, logging error message Failed parsing line... A simple stdout.remove(0) managed to fix it for me, but I did not edit your answer directly as I suspect it may vary on different devices ?Predigestion
I test the getForegroundApp() in Huawei P8 (Andriod Version 5.0.1). Also need to change if (lines.length != 2) to if (lines.length != 3) and String cpuaccctSubsystem = lines[1]; to String cpuaccctSubsystem = lines[2]; as @Eduardo Herzer did. In most cases it works except that it returns android.process.acore rather than the actual package name when dialer or contact is launched.Precipitation
github.com/android/platform_system_core/commit/…Phung
@JaredRummler what does it mean Enable hidepid=2 on /proc ? its low level change, not in Java, right?Vena
I can able to see the list of running apps. But how to identify which app is on top?Flagpole
Foreground app detection confirmed working on Asus Zenfone (5.1), Samsung Galaxy (5.0). One issue on the Asus Zenfone, is that the first item on the list is always com.mediatek.nlpservice, and the second one on the list is the correct answer: the foreground app. Questions: Do you expect this loophole to be closed in subsequent versions of android ? Will foreground app detection (not entire list of processes) be correct even when SE linux is enforcing ?Ule
Update on testing just foreground ap detection. Confirmed working on Samsung Note 4, Asus Zenfone, Samsung Galaxy S4. NOT working on HTC One 8, Sony Experia. Can others who have run tests please post their experiences?Ule
@JaredRummlerThanks for this. Unfortunately tested with the N preview and ProcessManager.getRunningForegroundApps(context) returns an empty string and ProcessManager.getRunningAppProcesses() only returns my app name.Resnick
@Resnick I'm aware that it isn't working on the N developer preview 1. I don't have the time right now to look into it and I'll most likely wait until preview 4 or final is released.Phung
getForegroundApp() always returns com.nuance.xt9.input (Keyboard App?), because it always has the oomscore value 1, which is lower than all other. Testes on Nexus 7 (2013), Android 6.0.1Meakem
foregroundProcess is always null on a Samsung Tab Pro 8.4 with Android 4.4.2 because cpuaccctSubsystem never ends with the pid. Why? Also encountered cases on a Nexus 5/M where Skype ends up with a higher oomscore than the launcher despite being in the foregroundIntosh
I've tested it on various phones and I can say that it works fairly good up to api 23. On my nexus 5X with android 7, the foreground app is always null. Also, is there any way of detecting services (overlays) like Facebook chatheads?Spyglass
Maybe you don't remember now but still if anyone else does that Is there any workaround for Nougat devices?Biddick
K
30
 private String printForegroundTask() {
    String currentApp = "NULL";
    if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
        UsageStatsManager usm = (UsageStatsManager)this.getSystemService("usagestats");
        long time = System.currentTimeMillis();
        List<UsageStats> appList = usm.queryUsageStats(UsageStatsManager.INTERVAL_DAILY,  time - 1000*1000, time);
        if (appList != null && appList.size() > 0) {
            SortedMap<Long, UsageStats> mySortedMap = new TreeMap<Long, UsageStats>();
            for (UsageStats usageStats : appList) {
                mySortedMap.put(usageStats.getLastTimeUsed(), usageStats);
            }
            if (mySortedMap != null && !mySortedMap.isEmpty()) {
                currentApp = mySortedMap.get(mySortedMap.lastKey()).getPackageName();
            }
        }
    } else {
        ActivityManager am = (ActivityManager)this.getSystemService(Context.ACTIVITY_SERVICE);
        List<ActivityManager.RunningAppProcessInfo> tasks = am.getRunningAppProcesses();
        currentApp = tasks.get(0).processName;
    }

    Log.e("adapter", "Current App in foreground is: " + currentApp);
    return currentApp;
}

Use this method for getting foreground task. U will need an System Permission "android:get_usage_stats"

public static boolean needPermissionForBlocking(Context context){
    try {
        PackageManager packageManager = context.getPackageManager();
        ApplicationInfo applicationInfo = packageManager.getApplicationInfo(context.getPackageName(), 0);
        AppOpsManager appOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
        int mode = appOpsManager.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS, applicationInfo.uid, applicationInfo.packageName);
        return  (mode != AppOpsManager.MODE_ALLOWED);
    } catch (PackageManager.NameNotFoundException e) {
        return true;
    }
}

IF user enable this in setting -> Security-> app with usage access. After that u will get foreground task. Similar process Clean matser by Cheetahamobile google play link

Kyrakyriako answered 1/9, 2015 at 11:28 Comment(11)
As pointed out in the OP and the comments, there are major downsides of using UsageStatsManager. Samsung has removed requests and it is quite annoying for a user to grant a system permission.Phung
I have tested in moto e2 device. It is working fine and yes, we need to implement this permission. we all facing same issues. We all really need a better approach. Good luck dudeKyrakyriako
This approach does not work at least on LG G3 because there is no "Settings -> Security-> App with usage access" menu itemSeriatim
That's not an answer to my question. Moreover, no new info is provided in this answer.Griselgriselda
how to kill all services here @Tarun SharmaVenue
Working fine but not properly in marshmallow. if notification came then current running process will be the package notification received. actually app is not running in foreground or background :(. need solution.Athletic
UsageStatsManager returns a populated list on Samsung S6/S7.Stanford
Your solution doesn't return anything on Pixel XL.Tauto
@Clocker& @Igor.. we can only play with UsageStatsManager to find out some solution. get running process is deprecated in marshmallow and aboveKyrakyriako
Go for accessibility permission if you want track user progress. but be aware accessibility permission is sensitive permissionKyrakyriako
How to get the app that i'm looking for goes to background?Baron
S
6

Take a look at https://github.com/ricvalerio/foregroundappchecker, it might be what you need. Provides sample code, and takes away the pain of having to implement cross version foreground detector.

Here are two samples:

AppChecker appChecker = new AppChecker();
String packageName = appChecker.getForegroundApp();

Or regularly check:

AppChecker appChecker = new AppChecker();
appChecker
    .when("com.other.app", new AppChecker.Listener() {
        @Override
        public void onForeground(String packageName) {
            // do something
        }
    )
    .when("com.my.app", new AppChecker.Listener() {
        @Override
        public void onForeground(String packageName) {
            // do something
        }
    )
    .other(new AppChecker.Listener() {
        @Override
        public void onForeground(String packageName) {
            // do something
        }
    )
    .timeout(1000)
    .start(this);
Smatter answered 28/8, 2016 at 23:6 Comment(2)
Thanks but nothing's new here. He just wrapped UsageStatsManager API which is mentioned in OP. The user will need to enable access, as other methods since Android 5.1.1Griselgriselda
@Jérémy did you request the required permissions?Smatter
J
4

Google limited this functionality for system apps only. As been reported in a bug ticket, you will need the REAL_GET_TASKS permission to access there.

Applications must now have ...permission.REAL_GET_TASKS to be able to get process information for all applications. Only the process information for the calling application will be returned if the app doesn't have the permission. Privileges apps will temporarily be able to get process information for all applications if they don't have the new permission, but have deprecated ...permission.GET_TASKS Also,only system apps can acquire the REAL_GET_TASKS permission.

Jefferyjeffie answered 2/9, 2015 at 15:9 Comment(2)
I saw applock is still working on 5.1.1, once the app get the permission in security->"apps with usage access"... This is somewhat surprisingLydell
"Permissions with the protection level signature, privileged or signatureOrSystem are only granted to system apps. If an app is a regular non-system app, it will never be able to use these permissions. " say android studio.. Good luck to negociate with Google to be accepted as a "system app".Abatis
K
4

Just throwing out a potential optimization to what I imagine is a heavily copy-pasted bit of code for detecting the top-most application on Android M.

This

if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
    UsageStatsManager usm = (UsageStatsManager)this.getSystemService("usagestats");
    long time = System.currentTimeMillis();
    List<UsageStats> appList = usm.queryUsageStats(UsageStatsManager.INTERVAL_DAILY,  time - 1000*1000, time);
    if (appList != null && appList.size() > 0) {
        SortedMap<Long, UsageStats> mySortedMap = new TreeMap<Long, UsageStats>();
        for (UsageStats usageStats : appList) {
            mySortedMap.put(usageStats.getLastTimeUsed(), usageStats);
        }
        if (mySortedMap != null && !mySortedMap.isEmpty()) {
            currentApp = mySortedMap.get(mySortedMap.lastKey()).getPackageName();
        }
    }
}

Can be simplified to this

if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
    UsageStatsManager usm = (UsageStatsManager) context.getSystemService(
        Context.USAGE_STATS_SERVICE);
    long time = System.currentTimeMillis();
    List<UsageStats> appStatsList = usm.queryUsageStats(UsageStatsManager.INTERVAL_DAILY,
            time - 1000 * 1000, time);
    if (appStatsList != null && !appStatsList.isEmpty()) {
        currentApp = Collections.max(appStatsList, (o1, o2) ->
            Long.compare(o1.getLastTimeUsed(), o2.getLastTimeUsed())).getPackageName();
    }
}

I found myself using this code in a 2 second loop, and wondered why I was using a complex solution that was O(n*log(n)) when a more simple solution was available in Collections.max() which is O(n).

Kathikathiawar answered 1/12, 2017 at 1:7 Comment(0)
J
0
public class AccessibilityDetectingService extends AccessibilityService {

@Override
protected void onServiceConnected() {
    super.onServiceConnected();

    //Configure these here for compatibility with API 13 and below.

    AccessibilityServiceInfo config = new AccessibilityServiceInfo();
    config.eventTypes = AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
    config.feedbackType = AccessibilityServiceInfo.FEEDBACK_GENERIC;

    if (Build.VERSION.SDK_INT >= 16)
        //Just in case this helps
        config.flags = AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;

    setServiceInfo(config);
}

@Override
public void onAccessibilityEvent(final AccessibilityEvent event) {
        if (event == null ) {
            return;
        } else if(event.getPackageName() == null && event.getClassName() == null){
            return;
        }

            if (activityInfo != null){

                Log.d("CurrentActivity", componentName.flattenToShortString());
        }

}

private ActivityInfo tryGetActivity(ComponentName componentName) {
    try {
        return getPackageManager().getActivityInfo(componentName, 0);
    } catch (PackageManager.NameNotFoundException e) {
        return null;
    }
}
@Override
public void onInterrupt() {
}                
}
}//`enter code here`uses-permission android:name="android.permission.BIND_ACCESSIBILITY_SERVICE" />
<uses-permission android:name="android.permission.GET_TASKS" />

Then start the service and app accessibility on in your device setting->accessibility->App on that service.

Janinajanine answered 2/2, 2016 at 11:22 Comment(1)
This is already covered in the question, and you've just copy-pasted the code from its original source on StackOverflow.Dosser
U
-4

Please try to use getRunningServices() instead of getRunningAppProcesses() method.

 ActivityManager mActivityManager = (ActivityManager) getSy stemService(Context.ACTIVITY_SERVICE);

 List<ActivityManager.RunningServiceInfo> appProcessInfoList = mActivityManager.getRunningServices(Integer.MAX_VALUE);
Unthoughtof answered 9/10, 2015 at 9:23 Comment(1)
Welcome to Stack Overflow! I don't think this will work since the foreground app might not be running a service.Dosser

© 2022 - 2024 — McMap. All rights reserved.