Android 5.1.1 і вище - getRunningAppProcess () повертає лише мій пакет програм


100

Здається, Google нарешті закрив усі двері для отримання поточного пакету програм переднього плану.

Після оновлення Lollipop, яке вбило getRunningTasks(int maxNum)і завдяки цій відповіді , я використовував цей код, щоб отримати пакет додатків переднього плану після 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 і пізніших версій (6.0 зефір), схоже, також загинув getRunningAppProcesses(). Тепер він повертає список вашого власного пакету програм.


UsageStatsManager

Ми можемо використовувати новий UsageStatsManagerAPI, як описано тут, але він працює не для всіх програм. Деякі системні програми повернуть той самий пакет

com.google.android.googlequicksearchbox

AccessibilityService (грудень 2017 року: заборонено використовувати Google)

Деякі програми використовують AccessibilityService(як це бачимо тут ), але це має деякі недоліки.


Чи є інший спосіб отримати поточний запущений пакет програм?


1
Du Speed ​​Booster, здається, працює. Я не впевнений, чи використовує він UsageStatsManager.
thecr0w

3
Зауважте, що кілька основних постачальників видалили системну діяльність, яка надає доступ до API UsageStats зі своїх пристроїв. Це означає, що програми на цих пристроях ніколи не можуть отримати необхідний дозвіл.
Кевін Крумвіде

3
Samsung є одним з них. Це понад 60% ринку.
Кевін Крумвіде

3
Ось квиток від андроїд-трек-помилок щодо цієї проблеми: code.google.com/p/android-developer-preview/isissue/…
Флоріан Барт

2
Якщо я розбираю результат запуску psв оболонці, а політика є, "fg"а вміст /proc/[pid]/oom_adj_scoreдорівнює, 0тоді додаток є програмою переднього плану. На жаль, /proc/[pid]/oom_adj_scoreна Андроїд 6.0 , здається, він більше не читається. gist.github.com/jaredrummler/7d1498485e584c8a120e
Джаред Раммлер

Відповіді:


93

Щоб отримати список запущених процесів на Android 1.6 - Android 6.0, ви можете використовувати цю бібліотеку, яку я написав: https://github.com/jaredrummler/AndroidProcess Бібліотека читає / proc, щоб отримати інформацію про процес.

Google значно обмежив доступ до / proc в Android Nougat. Щоб отримати список запущених процесів на Android Nougat, вам потрібно буде використовувати UsageStatsManager або мати кореневий доступ.

Клацніть історію редагування попередніх альтернативних рішень.


4
@androiddeveloper ні, я використовував лише libsuperuser, тому що це простий спосіб запустити команду оболонки (не-root або root) та отримати вихід. Я можу переписати його без лібсуперару, якщо є достатній попит.
Джаред Румлер

1
Вибачте за це. Просто цікаво було. :(
андроїд розробник

1
@JaredRummler Зараз я перебуваю на стадії впровадження вашого рішення у своєму додатку. Начебто добре працює, наскільки я можу сказати
guy.gc

1
@androiddeveloper. Якщо ви хочете сортувати його за іншим атрибутом, виконайте Processреалізацію Comparableта замініть compareToметод. Тоді, коли ви використовуєте Collections.sort(processesList), він використовуватиме вказаний вами порядок.
Шріні

2
@BruceWayne Я думаю, що Джаред посилається на це посилання: 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;
}

Використовуйте цей метод для отримання завдання переднього плану. Вам знадобиться системний дозвіл "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;
    }
}

ЯКЩО користувач увімкне це для налаштування -> Захист-> додаток із доступом до використання. Після цього ви отримаєте переднє завдання. Подібний процес Clean matser від Cheetahamobile google play link


Як було зазначено в ОП та коментарях, існують великі недоліки використання UsageStatsManager. Samsung видалила запити, і користувачеві це дуже прикро надати системний дозвіл.
Джаред Раммлер

Я протестував у пристрої moto e2. Це добре працює, і так, нам потрібно реалізувати цей дозвіл. всі ми стикаємося з однаковими проблемами. Нам усім дійсно потрібен кращий підхід. Чувак удачі
Тарун Шарма

2
Цей підхід не працює принаймні на LG G3, оскільки немає пункту меню "Налаштування -> Безпека-> Додаток із доступом до використання"
Дмитро

2
Це не відповідь на моє запитання. Більше того, у цій відповіді нової інформації немає.
Lior Iluz

1
Працює нормально, але не правильно в зефірі. якщо повідомлення надійшло, то поточним запущеним процесом буде отримане повідомлення про пакет. насправді додаток не працює на передньому плані чи на задньому плані :(. потрібно рішення.
WonderSoftwares

6

Погляньте на https://github.com/ricvalerio/foregroundappchecker , це може бути те, що вам потрібно. Надає зразок коду та позбавляє від необхідності реалізувати детектор переднього плану крос-версії.

Ось два зразки:

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

Або регулярно перевіряйте:

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

2
Дякую, але нічого нового тут немає. Він просто завершив API UsageStatsManager, який згадується в ОП. Користувачеві потрібно буде включити доступ, як і інші методи, оскільки Android 5.1.1
Lior Iluz

@ Jérémy, ти вимагав необхідних дозволів?
rvalerio

Здається, це також постійно опитується, правда?
андроїд розробник

4

Google обмежив цю функціональність лише для системних додатків. Як повідомлялося в квитку про помилку , вам потрібен дозвіл REAL_GET_TASKS для доступу туди.

Тепер програми повинні мати ... дозвіл.REAL_GET_TASKS, щоб мати змогу отримувати інформацію про процес для всіх програм. Повертається лише інформація про процес для виклику програми, якщо програма не має дозволу. Програми привілеїв тимчасово зможуть отримувати інформацію про процес для всіх програм, якщо вони не мають нового дозволу, але застаріли ... дозвіл.GET_TASKS Також дозвіл REAL_GET_TASKS також може отримати лише системні програми.


Я бачив, що додаток все ще працює 5.1.1, як тільки програма отримає дозвіл у безпеці -> "програми з доступом до використання" ... Це дещо дивно
mahesh ren

"Дозволи з підписом, привілеєм або підписомOrSystem надаються лише системним додаткам. Якщо додаток є звичайним несистемним додатком, він ніколи не зможе використовувати ці дозволи", - кажуть android studio. Google має бути прийнятим як "системний додаток".
Jérémy

2

Просто викинути потенційну оптимізацію до того, що я собі уявляю, є сильно скопійований біт коду для виявлення самого популярного додатка на Android M.

Це

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

Можна спростити це

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

Я знайшов себе за допомогою цього коду в циклі на 2 секунди, і задумався, чому я використовую складне рішення, яке було O (n * log (n)), коли в Collections.max () було більш просте рішення, яке O (n ).


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

Потім запустіть службу та доступність додатків у налаштуваннях свого пристрою-> доступність-> додаток у цій службі.


Це вже висвітлено у питанні, і ви просто скопіювали код із його оригінального джерела в StackOverflow.
Сем

-2

Спробуйте використовувати getRunningServices()замість getRunningAppProcesses()методу.

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

 List<ActivityManager.RunningServiceInfo> appProcessInfoList = mActivityManager.getRunningServices(Integer.MAX_VALUE);

3
Ласкаво просимо до переповнення стека! Я не думаю, що це спрацює, оскільки програма додаткового плану може не працювати з послугою.
Сем
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.