Android 5.1.1 und höher - getRunningAppProcesses () gibt nur mein Anwendungspaket zurück

100

Es scheint, dass Google endlich alle Türen geschlossen hat, um das aktuelle Vordergrundanwendungspaket zu erhalten.

Nach dem Lollipop-Update, das getötet wurde, getRunningTasks(int maxNum)und dank dieser Antwort habe ich diesen Code verwendet, um das Vordergrundanwendungspaket seit Lollipop zu erhalten:

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 und höher (6.0 Marshmallow) scheint ebenfalls getötet worden getRunningAppProcesses()zu sein. Es wird nun eine Liste Ihres eigenen Anwendungspakets zurückgegeben.


UsageStatsManager

Wir können die neue UsageStatsManagerAPI wie hier beschrieben verwenden , sie funktioniert jedoch nicht für alle Anwendungen. Einige Systemanwendungen geben dasselbe Paket zurück

com.google.android.googlequicksearchbox

AccessibilityService (Dezember 2017: Wird für die Verwendung durch Google gesperrt)

Einige Anwendungen verwenden AccessibilityService(wie hier zu sehen ), haben jedoch einige Nachteile.


Gibt es eine andere Möglichkeit, das aktuell ausgeführte Anwendungspaket abzurufen?

Lior Iluz
quelle
1
Du Speed ​​Booster scheint Arbeit zu sein. Ich bin nicht sicher, ob es UsageStatsManager verwendet.
thecr0w
3
Beachten Sie, dass mehrere große Anbieter die Systemaktivität, die den Zugriff auf die UsageStats-API gewährt, von ihren Geräten entfernt haben. Dies bedeutet, dass Apps auf diesen Geräten niemals die erforderliche Berechtigung erhalten können.
Kevin Krumwiede
3
Samsung ist einer von ihnen. Das sind über 60% des Marktes.
Kevin Krumwiede
3
Hier ist ein Ticket vom Android Bug Tracker zu diesem Problem: code.google.com/p/android-developer-preview/issues/…
Florian Barth
2
Wenn ich die Ausgabe der Ausführung psin einer Shell analysiere und die Richtlinie "fg"und der Inhalt von /proc/[pid]/oom_adj_scoregleich sind, 0ist die App die Vordergrundanwendung. Leider scheint /proc/[pid]/oom_adj_scorees unter Android 6.0 nicht mehr lesbar zu sein. gist.github.com/jaredrummler/7d1498485e584c8a120e
Jared Rummler

Antworten:

93

Um eine Liste der laufenden Prozesse unter Android 1.6 - Android 6.0 zu erhalten, können Sie diese Bibliothek verwenden, die ich geschrieben habe: https://github.com/jaredrummler/AndroidProcesses Die Bibliothek liest / proc, um Prozessinformationen abzurufen.

Google hat den Zugriff auf / proc in Android Nougat erheblich eingeschränkt. Um eine Liste der laufenden Prozesse auf Android Nougat zu erhalten, müssen Sie UsageStatsManager verwenden oder über Root-Zugriff verfügen.

Klicken Sie auf den Bearbeitungsverlauf für frühere alternative Lösungen.

Jared Rummler
quelle
4
@androiddeveloper nein, ich habe nur libsuperuser verwendet, weil es eine einfache Möglichkeit bietet, einen Shell-Befehl (nicht root oder root) auszuführen und die Ausgabe abzurufen. Ich kann es ohne libsuperuser neu schreiben, wenn genügend Nachfrage besteht.
Jared Rummler
1
Das tut mir leid. War nur neugierig. :(
Android Entwickler
1
@JaredRummler Ich bin gerade dabei, Ihre Lösung in meine App zu implementieren. Scheint gut zu funktionieren, soweit ich das
beurteilen
1
@androiddeveloper, Wenn Sie es nach einem anderen Attribut sortieren möchten, lassen Sie die Methode Processimplementieren Comparableund überschreiben compareTo. Wenn Sie dann verwenden Collections.sort(processesList), wird die von Ihnen angegebene Reihenfolge verwendet.
Srini
2
@ BruceWayne Ich denke, Jared verweist auf diesen Link: https://source.android.com/devices/tech/security/selinux/index.html
Eduardo Herzer
24
 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;
}

Verwenden Sie diese Methode, um die Vordergrundaufgabe abzurufen. Sie benötigen eine Systemberechtigung "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;
    }
}

WENN Benutzer dies in Einstellung -> Sicherheit-> App mit Nutzungszugriff aktivieren. Danach erhalten Sie eine Vordergrundaufgabe. Ähnliches Verfahren Clean Matser von Cheetahamobile Google Play Link

Tarun Sharma
quelle
Wie im OP und in den Kommentaren ausgeführt, gibt es große Nachteile bei der Verwendung UsageStatsManager. Samsung hat Anfragen entfernt und es ist ziemlich ärgerlich für einen Benutzer, eine Systemberechtigung zu erteilen.
Jared Rummler
Ich habe in moto e2 Gerät getestet. Es funktioniert gut und ja, wir müssen diese Berechtigung implementieren. wir alle stehen vor den gleichen Problemen. Wir alle brauchen wirklich einen besseren Ansatz. Viel Glück, Alter
Tarun Sharma
2
Dieser Ansatz funktioniert zumindest auf dem LG G3 nicht, da es keinen Menüpunkt "Einstellungen -> Sicherheit-> App mit Nutzungszugriff" gibt
Dmitry
2
Das ist keine Antwort auf meine Frage. Darüber hinaus enthält diese Antwort keine neuen Informationen.
Lior Iluz
1
Funktioniert gut, aber nicht richtig in Marshmallow. Wenn eine Benachrichtigung eingegangen ist, ist der aktuell ausgeführte Prozess die empfangene Paketbenachrichtigung. Eigentlich läuft die App nicht im Vordergrund oder Hintergrund :(. brauche Lösung.
WonderSoftwares
6

Werfen Sie einen Blick auf https://github.com/ricvalerio/foregroundappchecker , es könnte das sein, was Sie brauchen. Bietet Beispielcode und erspart Ihnen die Implementierung eines versionübergreifenden Vordergrunddetektors.

Hier sind zwei Beispiele:

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

Oder überprüfen Sie regelmäßig:

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);
rvalerio
quelle
2
Danke, aber hier ist nichts Neues. Er hat gerade die UsageStatsManager-API verpackt, die in OP erwähnt wird. Der Benutzer muss den Zugriff aktivieren, wie andere Methoden seit Android 5.1.1
Lior Iluz
@ Jérémy haben Sie die erforderlichen Berechtigungen angefordert?
Rvalerio
Scheint, als würde es auch die ganze Zeit abfragen, oder?
Android-Entwickler
4

Google hat diese Funktionalität nur für System-Apps eingeschränkt. Wie in einem Bug-Ticket gemeldet , benötigen Sie die Berechtigung REAL_GET_TASKS , um dort darauf zugreifen zu können.

Anwendungen müssen jetzt ... Berechtigung.REAL_GET_TASKS haben, um Prozessinformationen für alle Anwendungen abrufen zu können. Nur die Prozessinformationen für die aufrufende Anwendung werden zurückgegeben, wenn die App nicht über die Berechtigung verfügt. Berechtigungs-Apps können vorübergehend Prozessinformationen für alle Anwendungen abrufen, wenn sie nicht über die neue Berechtigung verfügen, aber veraltete ... Berechtigung.GET_TASKS Außerdem können nur System-Apps die Berechtigung REAL_GET_TASKS erhalten.

Ilya Gazman
quelle
Ich habe gesehen, dass Applock immer noch an 5.1.1 arbeitet, sobald die App die Berechtigung für Sicherheit erhalten hat -> "Apps mit Nutzungszugriff" ... Dies ist etwas überraschend
mahesh ren
"Berechtigungen mit der Signatur" Schutzstufe "," Privilegiert "oder" SignaturOrSystem "werden nur System-Apps erteilt. Wenn eine App eine reguläre Nicht-System-App ist, kann sie diese Berechtigungen niemals verwenden.", Sagt Android Studio. Viel Glück beim Verhandeln Google soll als "System-App" akzeptiert werden.
Jérémy
2

Nur eine mögliche Optimierung für das, was ich mir vorstelle, ist ein stark kopierter Code zum Erkennen der am häufigsten verwendeten Anwendung auf Android M.

Dies

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

Kann dazu vereinfacht werden

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

Ich stellte fest, dass ich diesen Code in einer 2-Sekunden-Schleife verwendete, und fragte mich, warum ich eine komplexe Lösung verwendete, die O (n * log (n)) war, als in Collections.max () eine einfachere Lösung verfügbar war, die O (n) ist ).

bstar55
quelle
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" />

Starten Sie dann den Dienst und die App-Zugänglichkeit in Ihrer Geräteeinstellung-> Eingabehilfen-> App für diesen Dienst.

Ram Bhawan Kushwaha
quelle
Dies wird bereits in der Frage behandelt, und Sie haben gerade den Code aus seiner ursprünglichen Quelle in StackOverflow kopiert.
Sam
-2

Bitte versuchen Sie es getRunningServices()anstelle der getRunningAppProcesses()Methode.

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

 List<ActivityManager.RunningServiceInfo> appProcessInfoList = mActivityManager.getRunningServices(Integer.MAX_VALUE);
Joe
quelle
3
Willkommen bei Stack Overflow! Ich denke nicht, dass dies funktionieren wird, da in der Vordergrund-App möglicherweise kein Dienst ausgeführt wird.
Sam